├── .gitignore ├── LICENSE ├── README.md ├── build └── output.yaml ├── package-lock.json ├── package.json ├── src ├── backend │ ├── _config │ │ └── config.json │ ├── _graphql │ │ ├── graphql-mapping-templates │ │ │ ├── request-mapping-template.txt │ │ │ └── response-mapping-template.txt │ │ └── graphql-schema │ │ │ └── schema.graphql │ ├── _mocks │ │ ├── assignOwner.json │ │ ├── aws-mock-generator │ │ │ ├── events │ │ │ │ └── identity.json │ │ │ ├── index.js │ │ │ └── sample-events │ │ │ │ ├── assign-owner-payload.json │ │ │ │ ├── cognito-create-user.json │ │ │ │ ├── get-companies-payload.json │ │ │ │ ├── get-contacts-payload.json │ │ │ │ ├── invite-user-payload.json │ │ │ │ ├── save-activity-from-entity-payload.json │ │ │ │ ├── save-activity-payload.json │ │ │ │ ├── save-company-payload.json │ │ │ │ ├── save-contact-payload.json │ │ │ │ ├── save-deal-from-entity-payload.json │ │ │ │ └── save-deal-payload.json │ │ ├── cognito-create-user.json │ │ ├── getCompanies.json │ │ ├── getContacts.json │ │ ├── inviteUser.json │ │ ├── saveActivity.json │ │ ├── saveActivityFromEntity.json │ │ ├── saveCompany.json │ │ ├── saveContact.json │ │ ├── saveDeal.json │ │ ├── saveDealFromEntity.json │ │ └── test.js │ ├── _utils │ │ ├── adapters │ │ │ ├── cognito-adapter.js │ │ │ └── dynamodb-adapter.js │ │ ├── parse │ │ │ ├── parse-request-arguments.js │ │ │ └── parse-user-identity.js │ │ ├── responses │ │ │ ├── process-response.js │ │ │ └── response-constructor.js │ │ └── tracker │ │ │ └── invocation-tracker.js │ ├── activities │ │ ├── assign-activity-to-entity.js │ │ ├── get-activities.js │ │ ├── save-activity-from-entity.js │ │ └── save-activity.js │ ├── companies │ │ ├── assign-company-to-entity.js │ │ ├── get-companies.js │ │ └── save-company.js │ ├── contacts │ │ ├── assign-contact-to-entity.js │ │ ├── get-contacts.js │ │ └── save-contact.js │ ├── deals │ │ ├── assign-deal-to-entity.js │ │ ├── get-deals.js │ │ ├── save-deal-from-entity.js │ │ └── save-deal.js │ ├── test │ │ └── unit │ │ │ ├── activities.test.js │ │ │ └── utils.test.js │ └── users │ │ ├── assign-owner-to-entity.js │ │ ├── get-cognito-user-list.js │ │ └── invite-user.js └── client │ └── .gitkeep └── template.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dimitri Tarasowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-crm 2 | Serverless CRM App based on AppSync and AWS Lambda 3 | -------------------------------------------------------------------------------- /build/output.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: Claudia CRM infrastrucuture as code - GraphQL Backend 3 | Globals: 4 | Function: 5 | Environment: 6 | Variables: 7 | TABLE_NAME: 8 | Ref: claudiaCrmMainDB 9 | USER_POOL_ID: 10 | Ref: claudiaCrmUserPool 11 | MemorySize: 128 12 | Runtime: nodejs8.10 13 | Timeout: 5 14 | Outputs: 15 | AppSyncGraphQLApiEndpoint: 16 | Description: Here is your GraphQL endpoint URL 17 | Value: 18 | Fn::GetAtt: 19 | - claudiaCrmAppSyncApi 20 | - GraphQLUrl 21 | AppSyncGraphQLArn: 22 | Description: Here is your GraphQL Arn 23 | Value: 24 | Fn::GetAtt: 25 | - claudiaCrmAppSyncApi 26 | - Arn 27 | Parameters: 28 | APIName: 29 | AllowedPattern: ^[a-zA-Z][a-zA-Z0-9_]*$ 30 | ConstraintDescription: The name should have min. 3 and max. 20 letters 31 | Default: claudiacrm 32 | Description: This is the API name 33 | MaxLength: 20 34 | MinLength: 3 35 | Type: String 36 | APIStage: 37 | AllowedPattern: ^[a-zA-Z][a-zA-Z0-9_]*$ 38 | AllowedValues: 39 | - prod 40 | - test 41 | - dev 42 | ConstraintDescription: Must be one of the following values - prod, test or dev 43 | Default: dev 44 | Description: This is the API stage 45 | MaxLength: 10 46 | MinLength: 3 47 | Type: String 48 | graphQLBucketName: 49 | AllowedPattern: ^[a-z0-9_.-]*$ 50 | ConstraintDescription: You need to create the bucket first, add schema and mapping 51 | templates 52 | Default: com.claudiacrm-dev-graphql-specs 53 | Description: This is the s3 url to the bucket with GraphQL Schema specs 54 | MaxLength: 50 55 | MinLength: 3 56 | Type: String 57 | Resources: 58 | appSyncInvokeLambdaFunction: 59 | Properties: 60 | Description: Managed policy to allow AppSync to invoke Lambda functions 61 | Path: /appsync/ 62 | PolicyDocument: 63 | Statement: 64 | - Action: 65 | - lambda:InvokeFunction 66 | Effect: Allow 67 | Resource: 68 | - Fn::GetAtt: 69 | - saveContact 70 | - Arn 71 | - Fn::GetAtt: 72 | - saveCompany 73 | - Arn 74 | - Fn::GetAtt: 75 | - saveDeal 76 | - Arn 77 | - Fn::GetAtt: 78 | - saveActivity 79 | - Arn 80 | - Fn::GetAtt: 81 | - getCognitoUserList 82 | - Arn 83 | - Fn::GetAtt: 84 | - saveDealFromEntity 85 | - Arn 86 | - Fn::GetAtt: 87 | - saveActivityFromEntity 88 | - Arn 89 | - Fn::GetAtt: 90 | - assignOwnerToEntity 91 | - Arn 92 | - Fn::GetAtt: 93 | - assignActivityToEntity 94 | - Arn 95 | - Fn::GetAtt: 96 | - getActivities 97 | - Arn 98 | - Fn::GetAtt: 99 | - assignDealToEntity 100 | - Arn 101 | - Fn::GetAtt: 102 | - getDeals 103 | - Arn 104 | - Fn::GetAtt: 105 | - assignCompanyToEntity 106 | - Arn 107 | - Fn::GetAtt: 108 | - getCompanies 109 | - Arn 110 | - Fn::GetAtt: 111 | - assignContactToEntity 112 | - Arn 113 | - Fn::GetAtt: 114 | - getContacts 115 | - Arn 116 | - Fn::GetAtt: 117 | - inviteUser 118 | - Arn 119 | Version: 2012-10-17 120 | Type: AWS::IAM::ManagedPolicy 121 | appSyncLambdaRole: 122 | DependsOn: 123 | - appSyncInvokeLambdaFunction 124 | Properties: 125 | AssumeRolePolicyDocument: 126 | Statement: 127 | - Action: 128 | - sts:AssumeRole 129 | Effect: Allow 130 | Principal: 131 | Service: 132 | - appsync.amazonaws.com 133 | Version: 2012-10-17 134 | ManagedPolicyArns: 135 | - Ref: appSyncInvokeLambdaFunction 136 | RoleName: 137 | Fn::Sub: ${APIName}-${APIStage}-appsync-invoke-lambda 138 | Type: AWS::IAM::Role 139 | assignActivityToEntity: 140 | Properties: 141 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 142 | Description: Lambda function that assigns activity to an entity 143 | FunctionName: 144 | Fn::Sub: ${APIName}-${APIStage}-assignActivityToEntity 145 | Handler: src/backend/activities/assign-activity-to-entity.handler 146 | Policies: 147 | - Statement: 148 | - Action: 149 | - dynamodb:putItem 150 | - dynamodb:Query 151 | - dynamodb:updateItem 152 | Effect: Allow 153 | Resource: 154 | Fn::GetAtt: 155 | - claudiaCrmMainDB 156 | - Arn 157 | Version: 2012-10-17 158 | Type: AWS::Serverless::Function 159 | assignActivityToEntityDataSource: 160 | Properties: 161 | ApiId: 162 | Fn::GetAtt: 163 | - claudiaCrmAppSyncApi 164 | - ApiId 165 | Description: Data source for assigning an activity from an entity view e.g. 166 | from contact, deal. 167 | LambdaConfig: 168 | LambdaFunctionArn: 169 | Fn::GetAtt: 170 | - assignActivityToEntity 171 | - Arn 172 | Name: 173 | Fn::Sub: ${APIName}assignActivityToEntity 174 | ServiceRoleArn: 175 | Fn::GetAtt: 176 | - appSyncLambdaRole 177 | - Arn 178 | Type: AWS_LAMBDA 179 | Type: AWS::AppSync::DataSource 180 | assignActivityToEntityResolver: 181 | DependsOn: claudiaCrmAppSyncSchema 182 | Properties: 183 | ApiId: 184 | Fn::GetAtt: 185 | - claudiaCrmAppSyncApi 186 | - ApiId 187 | DataSourceName: 188 | Fn::GetAtt: 189 | - assignActivityToEntityDataSource 190 | - Name 191 | FieldName: assignActivityToEntity 192 | RequestMappingTemplateS3Location: 193 | Fn::Join: 194 | - '' 195 | - - s3:// 196 | - Ref: graphQLBucketName 197 | - /request-mapping-template.txt 198 | ResponseMappingTemplateS3Location: 199 | Fn::Join: 200 | - '' 201 | - - s3:// 202 | - Ref: graphQLBucketName 203 | - /response-mapping-template.txt 204 | TypeName: Mutation 205 | Type: AWS::AppSync::Resolver 206 | assignCompanyToEntity: 207 | Properties: 208 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 209 | Description: Lambda function that assigns company to an entity 210 | FunctionName: 211 | Fn::Sub: ${APIName}-${APIStage}-assignCompanyToEntity 212 | Handler: src/backend/companies/assign-company-to-entity.handler 213 | Policies: 214 | - Statement: 215 | - Action: 216 | - dynamodb:putItem 217 | - dynamodb:Query 218 | - dynamodb:updateItem 219 | Effect: Allow 220 | Resource: 221 | Fn::GetAtt: 222 | - claudiaCrmMainDB 223 | - Arn 224 | Version: 2012-10-17 225 | Type: AWS::Serverless::Function 226 | assignCompanyToEntityDataSource: 227 | Properties: 228 | ApiId: 229 | Fn::GetAtt: 230 | - claudiaCrmAppSyncApi 231 | - ApiId 232 | Description: Data source for assigning a company from an entity view e.g. from 233 | contact, deal. 234 | LambdaConfig: 235 | LambdaFunctionArn: 236 | Fn::GetAtt: 237 | - assignCompanyToEntity 238 | - Arn 239 | Name: 240 | Fn::Sub: ${APIName}assignCompanyToEntity 241 | ServiceRoleArn: 242 | Fn::GetAtt: 243 | - appSyncLambdaRole 244 | - Arn 245 | Type: AWS_LAMBDA 246 | Type: AWS::AppSync::DataSource 247 | assignCompanyToEntityResolver: 248 | DependsOn: claudiaCrmAppSyncSchema 249 | Properties: 250 | ApiId: 251 | Fn::GetAtt: 252 | - claudiaCrmAppSyncApi 253 | - ApiId 254 | DataSourceName: 255 | Fn::GetAtt: 256 | - assignCompanyToEntityDataSource 257 | - Name 258 | FieldName: assignCompanyToEntity 259 | RequestMappingTemplateS3Location: 260 | Fn::Join: 261 | - '' 262 | - - s3:// 263 | - Ref: graphQLBucketName 264 | - /request-mapping-template.txt 265 | ResponseMappingTemplateS3Location: 266 | Fn::Join: 267 | - '' 268 | - - s3:// 269 | - Ref: graphQLBucketName 270 | - /response-mapping-template.txt 271 | TypeName: Mutation 272 | Type: AWS::AppSync::Resolver 273 | assignContactToEntity: 274 | Properties: 275 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 276 | Description: Lambda function that assigns contact to an entity 277 | FunctionName: 278 | Fn::Sub: ${APIName}-${APIStage}-assignContactToEntity 279 | Handler: src/backend/contacts/assign-contact-to-entity.handler 280 | Policies: 281 | - Statement: 282 | - Action: 283 | - dynamodb:putItem 284 | - dynamodb:Query 285 | - dynamodb:updateItem 286 | Effect: Allow 287 | Resource: 288 | Fn::GetAtt: 289 | - claudiaCrmMainDB 290 | - Arn 291 | Version: 2012-10-17 292 | Type: AWS::Serverless::Function 293 | assignContactToEntityDataSource: 294 | Properties: 295 | ApiId: 296 | Fn::GetAtt: 297 | - claudiaCrmAppSyncApi 298 | - ApiId 299 | Description: Data source for assigning a contact from an entity view e.g. from 300 | contact, deal. 301 | LambdaConfig: 302 | LambdaFunctionArn: 303 | Fn::GetAtt: 304 | - assignContactToEntity 305 | - Arn 306 | Name: 307 | Fn::Sub: ${APIName}assignContactToEntity 308 | ServiceRoleArn: 309 | Fn::GetAtt: 310 | - appSyncLambdaRole 311 | - Arn 312 | Type: AWS_LAMBDA 313 | Type: AWS::AppSync::DataSource 314 | assignContactToEntityResolver: 315 | DependsOn: claudiaCrmAppSyncSchema 316 | Properties: 317 | ApiId: 318 | Fn::GetAtt: 319 | - claudiaCrmAppSyncApi 320 | - ApiId 321 | DataSourceName: 322 | Fn::GetAtt: 323 | - assignContactToEntityDataSource 324 | - Name 325 | FieldName: assignContactToEntity 326 | RequestMappingTemplateS3Location: 327 | Fn::Join: 328 | - '' 329 | - - s3:// 330 | - Ref: graphQLBucketName 331 | - /request-mapping-template.txt 332 | ResponseMappingTemplateS3Location: 333 | Fn::Join: 334 | - '' 335 | - - s3:// 336 | - Ref: graphQLBucketName 337 | - /response-mapping-template.txt 338 | TypeName: Mutation 339 | Type: AWS::AppSync::Resolver 340 | assignDealToEntity: 341 | Properties: 342 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 343 | Description: Lambda function that assigns deal to an entity 344 | FunctionName: 345 | Fn::Sub: ${APIName}-${APIStage}-assignDealToEntity 346 | Handler: src/backend/deals/assign-deal-to-entity.handler 347 | Policies: 348 | - Statement: 349 | - Action: 350 | - dynamodb:putItem 351 | - dynamodb:Query 352 | - dynamodb:updateItem 353 | Effect: Allow 354 | Resource: 355 | Fn::GetAtt: 356 | - claudiaCrmMainDB 357 | - Arn 358 | Version: 2012-10-17 359 | Type: AWS::Serverless::Function 360 | assignDealToEntityDataSource: 361 | Properties: 362 | ApiId: 363 | Fn::GetAtt: 364 | - claudiaCrmAppSyncApi 365 | - ApiId 366 | Description: Data source for assigning a deal from an entity view e.g. from 367 | contact, deal. 368 | LambdaConfig: 369 | LambdaFunctionArn: 370 | Fn::GetAtt: 371 | - assignDealToEntity 372 | - Arn 373 | Name: 374 | Fn::Sub: ${APIName}assignDealToEntity 375 | ServiceRoleArn: 376 | Fn::GetAtt: 377 | - appSyncLambdaRole 378 | - Arn 379 | Type: AWS_LAMBDA 380 | Type: AWS::AppSync::DataSource 381 | assignDealToEntityResolver: 382 | DependsOn: claudiaCrmAppSyncSchema 383 | Properties: 384 | ApiId: 385 | Fn::GetAtt: 386 | - claudiaCrmAppSyncApi 387 | - ApiId 388 | DataSourceName: 389 | Fn::GetAtt: 390 | - assignDealToEntityDataSource 391 | - Name 392 | FieldName: assignDealToEntity 393 | RequestMappingTemplateS3Location: 394 | Fn::Join: 395 | - '' 396 | - - s3:// 397 | - Ref: graphQLBucketName 398 | - /request-mapping-template.txt 399 | ResponseMappingTemplateS3Location: 400 | Fn::Join: 401 | - '' 402 | - - s3:// 403 | - Ref: graphQLBucketName 404 | - /response-mapping-template.txt 405 | TypeName: Mutation 406 | Type: AWS::AppSync::Resolver 407 | assignOwnerToEntity: 408 | Properties: 409 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 410 | Description: Lambda function that assigns owner to an entity 411 | FunctionName: 412 | Fn::Sub: ${APIName}-${APIStage}-assignOwnerToEntity 413 | Handler: src/backend/users/assign-owner-to-entity.handler 414 | Policies: 415 | - Statement: 416 | - Action: 417 | - dynamodb:putItem 418 | - dynamodb:Query 419 | - dynamodb:updateItem 420 | Effect: Allow 421 | Resource: 422 | Fn::GetAtt: 423 | - claudiaCrmMainDB 424 | - Arn 425 | Version: 2012-10-17 426 | - Statement: 427 | - Action: 428 | - cognito-idp:ListUsers 429 | Effect: Allow 430 | Resource: 431 | Fn::GetAtt: 432 | - claudiaCrmUserPool 433 | - Arn 434 | Version: 2012-10-17 435 | Type: AWS::Serverless::Function 436 | assignOwnerToEntityDataSource: 437 | Properties: 438 | ApiId: 439 | Fn::GetAtt: 440 | - claudiaCrmAppSyncApi 441 | - ApiId 442 | Description: Data source for assigning owner from an entity view e.g. from contact, 443 | deal. 444 | LambdaConfig: 445 | LambdaFunctionArn: 446 | Fn::GetAtt: 447 | - assignOwnerToEntity 448 | - Arn 449 | Name: 450 | Fn::Sub: ${APIName}assignOwnerToEntity 451 | ServiceRoleArn: 452 | Fn::GetAtt: 453 | - appSyncLambdaRole 454 | - Arn 455 | Type: AWS_LAMBDA 456 | Type: AWS::AppSync::DataSource 457 | assignOwnerToEntityResolver: 458 | DependsOn: claudiaCrmAppSyncSchema 459 | Properties: 460 | ApiId: 461 | Fn::GetAtt: 462 | - claudiaCrmAppSyncApi 463 | - ApiId 464 | DataSourceName: 465 | Fn::GetAtt: 466 | - assignOwnerToEntityDataSource 467 | - Name 468 | FieldName: assignOwnerToEntity 469 | RequestMappingTemplateS3Location: 470 | Fn::Join: 471 | - '' 472 | - - s3:// 473 | - Ref: graphQLBucketName 474 | - /request-mapping-template.txt 475 | ResponseMappingTemplateS3Location: 476 | Fn::Join: 477 | - '' 478 | - - s3:// 479 | - Ref: graphQLBucketName 480 | - /response-mapping-template.txt 481 | TypeName: Mutation 482 | Type: AWS::AppSync::Resolver 483 | claudiaCrmAppSyncApi: 484 | Description: The GraphQL API for Claudia Crm 485 | Properties: 486 | AuthenticationType: AMAZON_COGNITO_USER_POOLS 487 | Name: 488 | Fn::Sub: ${APIName}-${APIStage} 489 | UserPoolConfig: 490 | AwsRegion: 491 | Fn::Sub: ${AWS::Region} 492 | DefaultAction: DENY 493 | UserPoolId: 494 | Ref: claudiaCrmUserPool 495 | Type: AWS::AppSync::GraphQLApi 496 | claudiaCrmAppSyncSchema: 497 | Properties: 498 | ApiId: 499 | Fn::GetAtt: 500 | - claudiaCrmAppSyncApi 501 | - ApiId 502 | DefinitionS3Location: 503 | Fn::Join: 504 | - '' 505 | - - s3:// 506 | - Ref: graphQLBucketName 507 | - /schema.graphql 508 | Type: AWS::AppSync::GraphQLSchema 509 | claudiaCrmMainDB: 510 | Description: The main table for contacts, companies, deals and activities 511 | Properties: 512 | AttributeDefinitions: 513 | - AttributeName: userId 514 | AttributeType: S 515 | - AttributeName: entityId 516 | AttributeType: S 517 | KeySchema: 518 | - AttributeName: userId 519 | KeyType: HASH 520 | - AttributeName: entityId 521 | KeyType: RANGE 522 | ProvisionedThroughput: 523 | ReadCapacityUnits: 1 524 | WriteCapacityUnits: 1 525 | TableName: 526 | Fn::Sub: ${APIName}-${APIStage} 527 | Type: AWS::DynamoDB::Table 528 | claudiaCrmPoolClient: 529 | Properties: 530 | UserPoolId: 531 | Ref: claudiaCrmUserPool 532 | Type: AWS::Cognito::UserPoolClient 533 | claudiaCrmUserPool: 534 | Properties: 535 | Schema: 536 | - Mutable: true 537 | Name: email 538 | Required: true 539 | - Mutable: true 540 | Name: preferred_username 541 | Required: true 542 | - Mutable: true 543 | Name: name 544 | Required: false 545 | - Mutable: true 546 | Name: family_name 547 | Required: false 548 | UserPoolName: 549 | Fn::Sub: ${APIName}-${APIStage} 550 | UserPoolTags: 551 | Name: Name 552 | Value: 553 | Fn::Sub: ${APIName}-${APIStage} 554 | UsernameAttributes: 555 | - email 556 | Type: AWS::Cognito::UserPool 557 | getActivities: 558 | Properties: 559 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 560 | Description: Lambda function that returns all activities 561 | FunctionName: 562 | Fn::Sub: ${APIName}-${APIStage}-getActivities 563 | Handler: src/backend/activities/get-activities.handler 564 | Policies: 565 | - Statement: 566 | - Action: 567 | - dynamodb:Query 568 | Effect: Allow 569 | Resource: 570 | Fn::GetAtt: 571 | - claudiaCrmMainDB 572 | - Arn 573 | Version: 2012-10-17 574 | Type: AWS::Serverless::Function 575 | getActivitiesDataSource: 576 | Properties: 577 | ApiId: 578 | Fn::GetAtt: 579 | - claudiaCrmAppSyncApi 580 | - ApiId 581 | Description: Data source for retrieving activities 582 | LambdaConfig: 583 | LambdaFunctionArn: 584 | Fn::GetAtt: 585 | - getActivities 586 | - Arn 587 | Name: 588 | Fn::Sub: ${APIName}getActivities 589 | ServiceRoleArn: 590 | Fn::GetAtt: 591 | - appSyncLambdaRole 592 | - Arn 593 | Type: AWS_LAMBDA 594 | Type: AWS::AppSync::DataSource 595 | getActivitiesResolver: 596 | DependsOn: claudiaCrmAppSyncSchema 597 | Properties: 598 | ApiId: 599 | Fn::GetAtt: 600 | - claudiaCrmAppSyncApi 601 | - ApiId 602 | DataSourceName: 603 | Fn::GetAtt: 604 | - getActivitiesDataSource 605 | - Name 606 | FieldName: getActivities 607 | RequestMappingTemplateS3Location: 608 | Fn::Join: 609 | - '' 610 | - - s3:// 611 | - Ref: graphQLBucketName 612 | - /request-mapping-template.txt 613 | ResponseMappingTemplateS3Location: 614 | Fn::Join: 615 | - '' 616 | - - s3:// 617 | - Ref: graphQLBucketName 618 | - /response-mapping-template.txt 619 | TypeName: Query 620 | Type: AWS::AppSync::Resolver 621 | getCognitoUserList: 622 | Properties: 623 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 624 | Description: Lambda function that returns all cognito users from a specific 625 | company 626 | FunctionName: 627 | Fn::Sub: ${APIName}-${APIStage}-getCognitoUserList 628 | Handler: src/backend/users/get-cognito-user-list.handler 629 | Policies: 630 | - Statement: 631 | - Action: 632 | - cognito-idp:ListUsers 633 | Effect: Allow 634 | Resource: 635 | Fn::GetAtt: 636 | - claudiaCrmUserPool 637 | - Arn 638 | Version: 2012-10-17 639 | Type: AWS::Serverless::Function 640 | getCognitoUserListDataSource: 641 | Properties: 642 | ApiId: 643 | Fn::GetAtt: 644 | - claudiaCrmAppSyncApi 645 | - ApiId 646 | Description: Data source for retrieving cognito users from the same company 647 | LambdaConfig: 648 | LambdaFunctionArn: 649 | Fn::GetAtt: 650 | - getCognitoUserList 651 | - Arn 652 | Name: 653 | Fn::Sub: ${APIName}getCognitoUserList 654 | ServiceRoleArn: 655 | Fn::GetAtt: 656 | - appSyncLambdaRole 657 | - Arn 658 | Type: AWS_LAMBDA 659 | Type: AWS::AppSync::DataSource 660 | getCognitoUserListResolver: 661 | DependsOn: claudiaCrmAppSyncSchema 662 | Properties: 663 | ApiId: 664 | Fn::GetAtt: 665 | - claudiaCrmAppSyncApi 666 | - ApiId 667 | DataSourceName: 668 | Fn::GetAtt: 669 | - getCognitoUserListDataSource 670 | - Name 671 | FieldName: getCognitoUserList 672 | RequestMappingTemplateS3Location: 673 | Fn::Join: 674 | - '' 675 | - - s3:// 676 | - Ref: graphQLBucketName 677 | - /request-mapping-template.txt 678 | ResponseMappingTemplateS3Location: 679 | Fn::Join: 680 | - '' 681 | - - s3:// 682 | - Ref: graphQLBucketName 683 | - /response-mapping-template.txt 684 | TypeName: Query 685 | Type: AWS::AppSync::Resolver 686 | getCompanies: 687 | Properties: 688 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 689 | Description: Lambda function that returns all companies 690 | FunctionName: 691 | Fn::Sub: ${APIName}-${APIStage}-getCompanies 692 | Handler: src/backend/companies/get-companies.handler 693 | Policies: 694 | - Statement: 695 | - Action: 696 | - dynamodb:Query 697 | Effect: Allow 698 | Resource: 699 | Fn::GetAtt: 700 | - claudiaCrmMainDB 701 | - Arn 702 | Version: 2012-10-17 703 | Type: AWS::Serverless::Function 704 | getCompaniesDataSource: 705 | Properties: 706 | ApiId: 707 | Fn::GetAtt: 708 | - claudiaCrmAppSyncApi 709 | - ApiId 710 | Description: Data source for retrieving companies 711 | LambdaConfig: 712 | LambdaFunctionArn: 713 | Fn::GetAtt: 714 | - getCompanies 715 | - Arn 716 | Name: 717 | Fn::Sub: ${APIName}getCompanies 718 | ServiceRoleArn: 719 | Fn::GetAtt: 720 | - appSyncLambdaRole 721 | - Arn 722 | Type: AWS_LAMBDA 723 | Type: AWS::AppSync::DataSource 724 | getCompaniesResolver: 725 | DependsOn: claudiaCrmAppSyncSchema 726 | Properties: 727 | ApiId: 728 | Fn::GetAtt: 729 | - claudiaCrmAppSyncApi 730 | - ApiId 731 | DataSourceName: 732 | Fn::GetAtt: 733 | - getCompaniesDataSource 734 | - Name 735 | FieldName: getCompanies 736 | RequestMappingTemplateS3Location: 737 | Fn::Join: 738 | - '' 739 | - - s3:// 740 | - Ref: graphQLBucketName 741 | - /request-mapping-template.txt 742 | ResponseMappingTemplateS3Location: 743 | Fn::Join: 744 | - '' 745 | - - s3:// 746 | - Ref: graphQLBucketName 747 | - /response-mapping-template.txt 748 | TypeName: Query 749 | Type: AWS::AppSync::Resolver 750 | getContacts: 751 | Properties: 752 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 753 | Description: Lambda function that returns all contacts 754 | FunctionName: 755 | Fn::Sub: ${APIName}-${APIStage}-getContacts 756 | Handler: src/backend/contacts/get-contacts.handler 757 | Policies: 758 | - Statement: 759 | - Action: 760 | - dynamodb:Query 761 | Effect: Allow 762 | Resource: 763 | Fn::GetAtt: 764 | - claudiaCrmMainDB 765 | - Arn 766 | Version: 2012-10-17 767 | Type: AWS::Serverless::Function 768 | getContactsDataSource: 769 | Properties: 770 | ApiId: 771 | Fn::GetAtt: 772 | - claudiaCrmAppSyncApi 773 | - ApiId 774 | Description: Data source for retrieving contacts 775 | LambdaConfig: 776 | LambdaFunctionArn: 777 | Fn::GetAtt: 778 | - getContacts 779 | - Arn 780 | Name: 781 | Fn::Sub: ${APIName}getContacts 782 | ServiceRoleArn: 783 | Fn::GetAtt: 784 | - appSyncLambdaRole 785 | - Arn 786 | Type: AWS_LAMBDA 787 | Type: AWS::AppSync::DataSource 788 | getContactsResolver: 789 | DependsOn: claudiaCrmAppSyncSchema 790 | Properties: 791 | ApiId: 792 | Fn::GetAtt: 793 | - claudiaCrmAppSyncApi 794 | - ApiId 795 | DataSourceName: 796 | Fn::GetAtt: 797 | - getContactsDataSource 798 | - Name 799 | FieldName: getContacts 800 | RequestMappingTemplateS3Location: 801 | Fn::Join: 802 | - '' 803 | - - s3:// 804 | - Ref: graphQLBucketName 805 | - /request-mapping-template.txt 806 | ResponseMappingTemplateS3Location: 807 | Fn::Join: 808 | - '' 809 | - - s3:// 810 | - Ref: graphQLBucketName 811 | - /response-mapping-template.txt 812 | TypeName: Query 813 | Type: AWS::AppSync::Resolver 814 | getDeals: 815 | Properties: 816 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 817 | Description: Lambda function that returns all deals 818 | FunctionName: 819 | Fn::Sub: ${APIName}-${APIStage}-getDeals 820 | Handler: src/backend/deals/get-deals.handler 821 | Policies: 822 | - Statement: 823 | - Action: 824 | - dynamodb:Query 825 | Effect: Allow 826 | Resource: 827 | Fn::GetAtt: 828 | - claudiaCrmMainDB 829 | - Arn 830 | Version: 2012-10-17 831 | Type: AWS::Serverless::Function 832 | getDealsDataSource: 833 | Properties: 834 | ApiId: 835 | Fn::GetAtt: 836 | - claudiaCrmAppSyncApi 837 | - ApiId 838 | Description: Data source for retrieving deals 839 | LambdaConfig: 840 | LambdaFunctionArn: 841 | Fn::GetAtt: 842 | - getDeals 843 | - Arn 844 | Name: 845 | Fn::Sub: ${APIName}getDeals 846 | ServiceRoleArn: 847 | Fn::GetAtt: 848 | - appSyncLambdaRole 849 | - Arn 850 | Type: AWS_LAMBDA 851 | Type: AWS::AppSync::DataSource 852 | getDealsResolver: 853 | DependsOn: claudiaCrmAppSyncSchema 854 | Properties: 855 | ApiId: 856 | Fn::GetAtt: 857 | - claudiaCrmAppSyncApi 858 | - ApiId 859 | DataSourceName: 860 | Fn::GetAtt: 861 | - getDealsDataSource 862 | - Name 863 | FieldName: getDeals 864 | RequestMappingTemplateS3Location: 865 | Fn::Join: 866 | - '' 867 | - - s3:// 868 | - Ref: graphQLBucketName 869 | - /request-mapping-template.txt 870 | ResponseMappingTemplateS3Location: 871 | Fn::Join: 872 | - '' 873 | - - s3:// 874 | - Ref: graphQLBucketName 875 | - /response-mapping-template.txt 876 | TypeName: Query 877 | Type: AWS::AppSync::Resolver 878 | inviteUser: 879 | Properties: 880 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 881 | Description: Lambda function that invites new users and assigns them to the 882 | right groups 883 | FunctionName: 884 | Fn::Sub: ${APIName}-${APIStage}-inviteUser 885 | Handler: src/backend/users/invite-user.handler 886 | Policies: 887 | - Statement: 888 | - Action: 889 | - cognito-idp:AdminCreateUser 890 | - cognito-idp:AdminAddUserToGroup 891 | Effect: Allow 892 | Resource: 893 | Fn::GetAtt: 894 | - claudiaCrmUserPool 895 | - Arn 896 | Version: 2012-10-17 897 | Type: AWS::Serverless::Function 898 | saveActivity: 899 | Properties: 900 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 901 | Description: Lambda function that handles the saving of an activity 902 | FunctionName: 903 | Fn::Sub: ${APIName}-${APIStage}-saveActivity 904 | Handler: src/backend/activities/save-activity.handler 905 | Policies: 906 | - Statement: 907 | - Action: 908 | - dynamodb:putItem 909 | Effect: Allow 910 | Resource: 911 | Fn::GetAtt: 912 | - claudiaCrmMainDB 913 | - Arn 914 | Version: 2012-10-17 915 | Type: AWS::Serverless::Function 916 | saveActivityDataSource: 917 | Properties: 918 | ApiId: 919 | Fn::GetAtt: 920 | - claudiaCrmAppSyncApi 921 | - ApiId 922 | Description: Data source for saving a contact to a database 923 | LambdaConfig: 924 | LambdaFunctionArn: 925 | Fn::GetAtt: 926 | - saveActivity 927 | - Arn 928 | Name: 929 | Fn::Sub: ${APIName}SaveActivity 930 | ServiceRoleArn: 931 | Fn::GetAtt: 932 | - appSyncLambdaRole 933 | - Arn 934 | Type: AWS_LAMBDA 935 | Type: AWS::AppSync::DataSource 936 | saveActivityFromEntity: 937 | Properties: 938 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 939 | Description: 'Lambda function that saves an activity from within an entity e.g. 940 | contact or company. Especially you need it when your are inside a contact 941 | and want to save some activity. You don''t need to switch a view to create 942 | an activity to come back to assign one. 943 | 944 | ' 945 | FunctionName: 946 | Fn::Sub: ${APIName}-${APIStage}-saveActivityFromEntity 947 | Handler: src/backend/activities/save-activity-from-entity.handler 948 | Policies: 949 | - Statement: 950 | - Action: 951 | - dynamodb:putItem 952 | - dynamodb:Query 953 | - dynamodb:updateItem 954 | Effect: Allow 955 | Resource: 956 | Fn::GetAtt: 957 | - claudiaCrmMainDB 958 | - Arn 959 | Version: 2012-10-17 960 | Type: AWS::Serverless::Function 961 | saveActivityFromEntityDataSource: 962 | Properties: 963 | ApiId: 964 | Fn::GetAtt: 965 | - claudiaCrmAppSyncApi 966 | - ApiId 967 | Description: Data source for saving an activity from an entity view e.g. from 968 | contact, deal. 969 | LambdaConfig: 970 | LambdaFunctionArn: 971 | Fn::GetAtt: 972 | - saveActivityFromEntity 973 | - Arn 974 | Name: 975 | Fn::Sub: ${APIName}saveActivityFromEntity 976 | ServiceRoleArn: 977 | Fn::GetAtt: 978 | - appSyncLambdaRole 979 | - Arn 980 | Type: AWS_LAMBDA 981 | Type: AWS::AppSync::DataSource 982 | saveActivityFromEntityResolver: 983 | DependsOn: claudiaCrmAppSyncSchema 984 | Properties: 985 | ApiId: 986 | Fn::GetAtt: 987 | - claudiaCrmAppSyncApi 988 | - ApiId 989 | DataSourceName: 990 | Fn::GetAtt: 991 | - saveActivityFromEntityDataSource 992 | - Name 993 | FieldName: saveActivityFromEntity 994 | RequestMappingTemplateS3Location: 995 | Fn::Join: 996 | - '' 997 | - - s3:// 998 | - Ref: graphQLBucketName 999 | - /request-mapping-template.txt 1000 | ResponseMappingTemplateS3Location: 1001 | Fn::Join: 1002 | - '' 1003 | - - s3:// 1004 | - Ref: graphQLBucketName 1005 | - /response-mapping-template.txt 1006 | TypeName: Mutation 1007 | Type: AWS::AppSync::Resolver 1008 | saveActivityResolver: 1009 | DependsOn: claudiaCrmAppSyncSchema 1010 | Properties: 1011 | ApiId: 1012 | Fn::GetAtt: 1013 | - claudiaCrmAppSyncApi 1014 | - ApiId 1015 | DataSourceName: 1016 | Fn::GetAtt: 1017 | - saveActivityDataSource 1018 | - Name 1019 | FieldName: saveActivity 1020 | RequestMappingTemplateS3Location: 1021 | Fn::Join: 1022 | - '' 1023 | - - s3:// 1024 | - Ref: graphQLBucketName 1025 | - /request-mapping-template.txt 1026 | ResponseMappingTemplateS3Location: 1027 | Fn::Join: 1028 | - '' 1029 | - - s3:// 1030 | - Ref: graphQLBucketName 1031 | - /response-mapping-template.txt 1032 | TypeName: Mutation 1033 | Type: AWS::AppSync::Resolver 1034 | saveCompany: 1035 | Properties: 1036 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 1037 | Description: Lambda function that handles the saving of a company 1038 | FunctionName: 1039 | Fn::Sub: ${APIName}-${APIStage}-saveCompany 1040 | Handler: src/backend/companies/save-company.handler 1041 | Policies: 1042 | - Statement: 1043 | - Action: 1044 | - dynamodb:putItem 1045 | Effect: Allow 1046 | Resource: 1047 | Fn::GetAtt: 1048 | - claudiaCrmMainDB 1049 | - Arn 1050 | Version: 2012-10-17 1051 | Type: AWS::Serverless::Function 1052 | saveCompanyDataSource: 1053 | Properties: 1054 | ApiId: 1055 | Fn::GetAtt: 1056 | - claudiaCrmAppSyncApi 1057 | - ApiId 1058 | Description: Data source for saving a contact to a database 1059 | LambdaConfig: 1060 | LambdaFunctionArn: 1061 | Fn::GetAtt: 1062 | - saveCompany 1063 | - Arn 1064 | Name: 1065 | Fn::Sub: ${APIName}SaveCompany 1066 | ServiceRoleArn: 1067 | Fn::GetAtt: 1068 | - appSyncLambdaRole 1069 | - Arn 1070 | Type: AWS_LAMBDA 1071 | Type: AWS::AppSync::DataSource 1072 | saveCompanyResolver: 1073 | DependsOn: claudiaCrmAppSyncSchema 1074 | Properties: 1075 | ApiId: 1076 | Fn::GetAtt: 1077 | - claudiaCrmAppSyncApi 1078 | - ApiId 1079 | DataSourceName: 1080 | Fn::GetAtt: 1081 | - saveCompanyDataSource 1082 | - Name 1083 | FieldName: saveCompany 1084 | RequestMappingTemplateS3Location: 1085 | Fn::Join: 1086 | - '' 1087 | - - s3:// 1088 | - Ref: graphQLBucketName 1089 | - /request-mapping-template.txt 1090 | ResponseMappingTemplateS3Location: 1091 | Fn::Join: 1092 | - '' 1093 | - - s3:// 1094 | - Ref: graphQLBucketName 1095 | - /response-mapping-template.txt 1096 | TypeName: Mutation 1097 | Type: AWS::AppSync::Resolver 1098 | saveContact: 1099 | Properties: 1100 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 1101 | Description: Lambda function that handles the saving of a contact 1102 | FunctionName: 1103 | Fn::Sub: ${APIName}-${APIStage}-saveContact 1104 | Handler: src/backend/contacts/save-contact.handler 1105 | Policies: 1106 | - Statement: 1107 | - Action: 1108 | - dynamodb:putItem 1109 | Effect: Allow 1110 | Resource: 1111 | Fn::GetAtt: 1112 | - claudiaCrmMainDB 1113 | - Arn 1114 | Version: 2012-10-17 1115 | Type: AWS::Serverless::Function 1116 | saveContactDataSource: 1117 | Properties: 1118 | ApiId: 1119 | Fn::GetAtt: 1120 | - claudiaCrmAppSyncApi 1121 | - ApiId 1122 | Description: Data source for saving a contact to a database 1123 | LambdaConfig: 1124 | LambdaFunctionArn: 1125 | Fn::GetAtt: 1126 | - saveContact 1127 | - Arn 1128 | Name: 1129 | Fn::Sub: ${APIName}SaveContact 1130 | ServiceRoleArn: 1131 | Fn::GetAtt: 1132 | - appSyncLambdaRole 1133 | - Arn 1134 | Type: AWS_LAMBDA 1135 | Type: AWS::AppSync::DataSource 1136 | saveContactResolver: 1137 | DependsOn: claudiaCrmAppSyncSchema 1138 | Properties: 1139 | ApiId: 1140 | Fn::GetAtt: 1141 | - claudiaCrmAppSyncApi 1142 | - ApiId 1143 | DataSourceName: 1144 | Fn::GetAtt: 1145 | - saveContactDataSource 1146 | - Name 1147 | FieldName: saveContact 1148 | RequestMappingTemplateS3Location: 1149 | Fn::Join: 1150 | - '' 1151 | - - s3:// 1152 | - Ref: graphQLBucketName 1153 | - /request-mapping-template.txt 1154 | ResponseMappingTemplateS3Location: 1155 | Fn::Join: 1156 | - '' 1157 | - - s3:// 1158 | - Ref: graphQLBucketName 1159 | - /response-mapping-template.txt 1160 | TypeName: Mutation 1161 | Type: AWS::AppSync::Resolver 1162 | saveDeal: 1163 | Properties: 1164 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 1165 | Description: Lambda function that handles the saving of a deal 1166 | FunctionName: 1167 | Fn::Sub: ${APIName}-${APIStage}-saveDeal 1168 | Handler: src/backend/deals/save-deal.handler 1169 | Policies: 1170 | - Statement: 1171 | - Action: 1172 | - dynamodb:putItem 1173 | Effect: Allow 1174 | Resource: 1175 | Fn::GetAtt: 1176 | - claudiaCrmMainDB 1177 | - Arn 1178 | Version: 2012-10-17 1179 | Type: AWS::Serverless::Function 1180 | saveDealDataSource: 1181 | Properties: 1182 | ApiId: 1183 | Fn::GetAtt: 1184 | - claudiaCrmAppSyncApi 1185 | - ApiId 1186 | Description: Data source for saving a contact to a database 1187 | LambdaConfig: 1188 | LambdaFunctionArn: 1189 | Fn::GetAtt: 1190 | - saveDeal 1191 | - Arn 1192 | Name: 1193 | Fn::Sub: ${APIName}SaveDeal 1194 | ServiceRoleArn: 1195 | Fn::GetAtt: 1196 | - appSyncLambdaRole 1197 | - Arn 1198 | Type: AWS_LAMBDA 1199 | Type: AWS::AppSync::DataSource 1200 | saveDealFromEntity: 1201 | Properties: 1202 | CodeUri: s3://com.claudiacrm-deployment-bucket-dev-new/e10ffb5153a6b74b6482c35e5cd682ee 1203 | Description: 'Lambda function that saves an deal from within an entity e.g. 1204 | contact or company. Especially you need it when your are inside a contact 1205 | and want to save a new deal. You don''t need to switch the view to create 1206 | a deal to come back to assign one. 1207 | 1208 | ' 1209 | FunctionName: 1210 | Fn::Sub: ${APIName}-${APIStage}-saveDealFromEntity 1211 | Handler: src/backend/deals/save-deal-from-entity.handler 1212 | Policies: 1213 | - Statement: 1214 | - Action: 1215 | - dynamodb:putItem 1216 | - dynamodb:Query 1217 | - dynamodb:updateItem 1218 | Effect: Allow 1219 | Resource: 1220 | Fn::GetAtt: 1221 | - claudiaCrmMainDB 1222 | - Arn 1223 | Version: 2012-10-17 1224 | Type: AWS::Serverless::Function 1225 | saveDealFromEntityDataSource: 1226 | Properties: 1227 | ApiId: 1228 | Fn::GetAtt: 1229 | - claudiaCrmAppSyncApi 1230 | - ApiId 1231 | Description: Data source for saving a deal from an entity view e.g. from contact, 1232 | deal. 1233 | LambdaConfig: 1234 | LambdaFunctionArn: 1235 | Fn::GetAtt: 1236 | - saveDealFromEntity 1237 | - Arn 1238 | Name: 1239 | Fn::Sub: ${APIName}saveDealFromEntity 1240 | ServiceRoleArn: 1241 | Fn::GetAtt: 1242 | - appSyncLambdaRole 1243 | - Arn 1244 | Type: AWS_LAMBDA 1245 | Type: AWS::AppSync::DataSource 1246 | saveDealFromEntityResolver: 1247 | DependsOn: claudiaCrmAppSyncSchema 1248 | Properties: 1249 | ApiId: 1250 | Fn::GetAtt: 1251 | - claudiaCrmAppSyncApi 1252 | - ApiId 1253 | DataSourceName: 1254 | Fn::GetAtt: 1255 | - saveDealFromEntityDataSource 1256 | - Name 1257 | FieldName: saveDealFromEntity 1258 | RequestMappingTemplateS3Location: 1259 | Fn::Join: 1260 | - '' 1261 | - - s3:// 1262 | - Ref: graphQLBucketName 1263 | - /request-mapping-template.txt 1264 | ResponseMappingTemplateS3Location: 1265 | Fn::Join: 1266 | - '' 1267 | - - s3:// 1268 | - Ref: graphQLBucketName 1269 | - /response-mapping-template.txt 1270 | TypeName: Mutation 1271 | Type: AWS::AppSync::Resolver 1272 | saveDealResolver: 1273 | DependsOn: claudiaCrmAppSyncSchema 1274 | Properties: 1275 | ApiId: 1276 | Fn::GetAtt: 1277 | - claudiaCrmAppSyncApi 1278 | - ApiId 1279 | DataSourceName: 1280 | Fn::GetAtt: 1281 | - saveDealDataSource 1282 | - Name 1283 | FieldName: saveDeal 1284 | RequestMappingTemplateS3Location: 1285 | Fn::Join: 1286 | - '' 1287 | - - s3:// 1288 | - Ref: graphQLBucketName 1289 | - /request-mapping-template.txt 1290 | ResponseMappingTemplateS3Location: 1291 | Fn::Join: 1292 | - '' 1293 | - - s3:// 1294 | - Ref: graphQLBucketName 1295 | - /response-mapping-template.txt 1296 | TypeName: Mutation 1297 | Type: AWS::AppSync::Resolver 1298 | Transform: AWS::Serverless-2016-10-31 1299 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claudiacrm", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "uuid": { 8 | "version": "3.3.2", 9 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 10 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claudiacrm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "No testing yet", 8 | "folder": "rm -rf build && mkdir build", 9 | "bucket": "aws s3 mb s3://com.claudiacrm-deployment-bucket-dev-new --region eu-west-1", 10 | "schema-update": "aws s3 cp ./src/backend/_graphql/graphql-schema/schema.graphql s3://com.claudiacrm-dev-graphql-specs", 11 | "package": "aws cloudformation package --template-file template.yaml --output-template-file build/output.yaml --s3-bucket com.claudiacrm-deployment-bucket-dev-new", 12 | "deploy": "aws cloudformation deploy --template-file build/output.yaml --stack-name claudia-crm-dev --capabilities CAPABILITY_NAMED_IAM", 13 | "qd": "npm run schema-update && npm run folder && npm run package && npm run deploy" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "" 18 | }, 19 | "keywords": [], 20 | "author": "", 21 | "license": "ISC", 22 | "dependencies": { 23 | "uuid": "^3.3.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/backend/_config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "cognitoUserPoolId": "eu-west-1_Ez3lS4Q8O", 3 | "dynamodbTableName": "claudiacrm-dev" 4 | } -------------------------------------------------------------------------------- /src/backend/_graphql/graphql-mapping-templates/request-mapping-template.txt: -------------------------------------------------------------------------------- 1 | #set($myMap = { 2 | "args": $context.arguments, 3 | "identity": $context.identity 4 | }) 5 | 6 | { 7 | "version" : "2017-02-28", 8 | "operation": "Invoke", 9 | "payload": $util.toJson($myMap) 10 | } -------------------------------------------------------------------------------- /src/backend/_graphql/graphql-mapping-templates/response-mapping-template.txt: -------------------------------------------------------------------------------- 1 | $util.toJson($context.result) -------------------------------------------------------------------------------- /src/backend/_graphql/graphql-schema/schema.graphql: -------------------------------------------------------------------------------- 1 | type Mutation { 2 | saveContact(input: contactAttributes!): String 3 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 4 | saveCompany(input: companyAttributes!): String 5 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 6 | saveDeal(input: dealAttributes!): String 7 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 8 | saveDealFromEntity(input: dealAttributes!): String 9 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 10 | # This is when you create a deal and assign it to a contact or company 11 | saveActivity(input: activityAttributes!): String 12 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 13 | saveActivityFromEntity(input: activityAttributes!): String 14 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 15 | # This is when you save an activity from contact or company or deal 16 | assignOwnerToEntity(entityId: String!, ownerUserId: String!): String 17 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 18 | assignCompanyToEntity(entityId: String!, companyEntityId: String!): String 19 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 20 | assignContactToEntity(entityId: String!, contactEntityId: String!): String 21 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 22 | assignDealToEntity(entityId: String!, dealEntityId: String!): String 23 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 24 | assignActivityToEntity(entityId: String!, activityEntityId: String!): String 25 | @aws_auth(cognito_groups: ["hasFullAccess","canEditEverything","canEditOwnerOnly"]) 26 | inviteUser(userEmail: String!, input: RBACList!): String 27 | @aws_auth(cognito_groups: ["hasFullAccess","canInvite"]) 28 | } 29 | 30 | type Query { 31 | getContacts(entity: entityEnum!): [allContacts] 32 | @aws_auth(cognito_groups: ["hasFullAccess","canViewEverything","canViewOwnerOnly"]) 33 | getCompanies(entity: entityEnum!): [allCompanies] 34 | @aws_auth(cognito_groups: ["hasFullAccess","canViewEverything","canViewOwnerOnly"]) 35 | getDeals(entity: entityEnum!): [allDeals] 36 | @aws_auth(cognito_groups: ["hasFullAccess","canViewEverything","canViewOwnerOnly"]) 37 | getActivities(entity: entityEnum!): [allActivities] 38 | @aws_auth(cognito_groups: ["hasFullAccess","canViewEverything","canViewOwnerOnly"]) 39 | getCognitoUserList: [ownerList] 40 | @aws_auth(cognito_groups: ["hasFullAccess","canViewEverything","canViewOwnerOnly"]) 41 | } 42 | 43 | input RBACList { 44 | canViewEverything: accessEnum! 45 | canViewOwnerOnly: accessEnum! 46 | canEditEverything: accessEnum! 47 | canEditOwnerOnly: accessEnum! 48 | canImport: accessEnum! 49 | canExport: accessEnum! 50 | canInvite: accessEnum! 51 | hasFullAccess: accessEnum! 52 | } 53 | 54 | enum accessEnum { 55 | yes 56 | no 57 | } 58 | 59 | input activityAttributes { 60 | activityType: activityTypeEnum! 61 | entity: entityEnum! 62 | activityName: String! 63 | assignToEntityId: String 64 | } 65 | 66 | type activityDetails { 67 | userId: String 68 | entityId: String 69 | companyId: String 70 | activityType: String 71 | name: String 72 | createDate: String 73 | entity: String! 74 | } 75 | 76 | enum activityTypeEnum { 77 | email 78 | call 79 | todo 80 | meeting 81 | } 82 | 83 | type allActivities { 84 | userId: String 85 | entityId: String 86 | entity: String! 87 | activityType: String 88 | name: String 89 | createDate: String 90 | assignedToEntityId: String 91 | } 92 | 93 | type allCompanies { 94 | userId: String 95 | entityId: String 96 | companyId: String 97 | name: String 98 | email: String 99 | createDate: String 100 | entity: String! 101 | assignedActivities: allActivities 102 | } 103 | 104 | type allCompanyContacts { 105 | companyId: String 106 | entity: String 107 | userId: String 108 | entityId: String 109 | name: String 110 | email: String 111 | createDate: String 112 | ownerId: String 113 | ownerDetails: ownerDetails 114 | } 115 | 116 | type allContacts { 117 | userId: String! 118 | entityId: String! 119 | companyId: String! 120 | name: String 121 | email: String 122 | createDate: String 123 | entity: String! 124 | ownerId: String 125 | ownerDetails: ownerDetails 126 | dealId: [String] 127 | dealDetails: [dealDetails] 128 | activityId: [String] 129 | activityDetails: [activityDetails] 130 | associatedCompanyId: String 131 | associatedCompanyDetails: companyDetails 132 | } 133 | 134 | type allDeals { 135 | userId: String 136 | entityId: String 137 | companyId: String 138 | name: String 139 | value: Int 140 | createDate: String 141 | entity: String! 142 | } 143 | 144 | input companyAttributes { 145 | entity: entityEnum! 146 | companyName: String! 147 | companyEmail: String 148 | } 149 | 150 | type companyDetails { 151 | name: String 152 | email: String 153 | entity: String 154 | entityId: String 155 | userId: String 156 | companyId: String 157 | createDate: String 158 | } 159 | 160 | input contactAttributes { 161 | entity: entityEnum! 162 | contactName: String! 163 | contactEmail: String 164 | } 165 | 166 | input dealAttributes { 167 | entity: entityEnum! 168 | dealName: String! 169 | dealValue: Int 170 | assignToEntityId: String 171 | } 172 | 173 | type dealDetails { 174 | entity: String 175 | value: Int 176 | createDate: String 177 | userId: String 178 | companyId: String 179 | name: String 180 | entityId: String 181 | } 182 | 183 | enum entityEnum { 184 | contact 185 | company 186 | deal 187 | activity 188 | } 189 | 190 | type ownerDetails { 191 | firstName: String 192 | lastName: String 193 | userId: String 194 | companyId: String 195 | email: String 196 | } 197 | 198 | type ownerList { 199 | userId: String! 200 | companyId: String! 201 | firstName: String 202 | lastName: String 203 | email: String! 204 | } 205 | 206 | schema { 207 | query: Query 208 | mutation: Mutation 209 | } -------------------------------------------------------------------------------- /src/backend/_mocks/assignOwner.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "entityId": "contact_a70b8577-cc1e-4f18-81e7-b295f055037f", 4 | "ownerUserId": "18b560d3-b524-4e11-a5cc-9aa46dc31350" 5 | }, 6 | "identity": { 7 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 8 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 9 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 10 | "claims": { 11 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 12 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 13 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 14 | "preferred_username": "testcompany", 15 | "aud": "vagd8sae0kg9sm8l829trej5i", 16 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 17 | "token_use": "id", 18 | "auth_time": 1531319357, 19 | "name": "Dimitri", 20 | "exp": 1531322957, 21 | "iat": 1531319357, 22 | "family_name": "Tarasowski", 23 | "email": "dimitri+test@tarasowski.de" 24 | }, 25 | "sourceIp": [ 26 | "37.221.177.4" 27 | ], 28 | "defaultAuthStrategy": "ALLOW", 29 | "groups": null 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/events/identity.json: -------------------------------------------------------------------------------- 1 | { 2 | "identity": { 3 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 4 | "issuer": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_Ez3lS4Q8O", 5 | "username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 6 | "claims": { 7 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 8 | "cognito:groups": [ 9 | "canInvite", 10 | "hasFullAccess" 11 | ], 12 | "iss": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_Ez3lS4Q8O", 13 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 14 | "preferred_username": "testcompany", 15 | "aud": "4ovlg9o3ahg7703d2aadgfojlg", 16 | "event_id": "338bbb06-8f49-11e8-a316-5d4eb5239c30", 17 | "token_use": "id", 18 | "auth_time": 1532440519, 19 | "name": "Dimitri", 20 | "exp": 1532444119, 21 | "iat": 1532440519, 22 | "family_name": "Tarasowski", 23 | "email": "dimitri+test@tarasowski.de" 24 | }, 25 | "sourceIp": [ 26 | "37.221.176.67" 27 | ], 28 | "defaultAuthStrategy": "DENY", 29 | "groups": [ 30 | "canInvite", 31 | "hasFullAccess" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/index.js: -------------------------------------------------------------------------------- 1 | const identity = require('./events/identity.json') 2 | 3 | 4 | const dictionary = { 5 | 'aws:appsync': true, 6 | 'aws:s3': false, 7 | 'aws:sns': false 8 | } 9 | 10 | const argumentsConstructor = (inputArguments) => { 11 | const argumentsConstructor = [] 12 | 13 | for (let key in inputArguments) { 14 | argumentsConstructor.push({[key]: inputArguments[key]}) 15 | } 16 | 17 | const constructedObject = {} 18 | constructedObject.args = {} 19 | constructedObject.args['input'] = {} 20 | 21 | argumentsConstructor.forEach(element => { 22 | if (!element.settings) 23 | Object.assign(constructedObject.args['input'], element) 24 | }) 25 | 26 | return constructedObject 27 | } 28 | 29 | const identityConstructor = (inputArguments) => { 30 | if (inputArguments) { 31 | return identity 32 | } else { 33 | return null 34 | } 35 | } 36 | 37 | 38 | const CreateEvent = function (inputArguments) { 39 | if (!dictionary[inputArguments.templateName]) { 40 | console.log('Template is not available') 41 | return 'Template is not available' 42 | } else { 43 | const customInput = argumentsConstructor(inputArguments.customInput) 44 | const identityFlag = identityConstructor(inputArguments.cognitoIdentity) 45 | 46 | if(customInput && identityFlag) { 47 | const constructedObject = Object.assign(customInput, identity) 48 | return constructedObject 49 | } else if (!identityFlag) { 50 | return customInput 51 | } else { 52 | return new Error('Something is wrong') 53 | } 54 | 55 | } 56 | } 57 | 58 | module.exports = CreateEvent 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/assign-owner-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "entityId": "contact_a70b8577-cc1e-4f18-81e7-b295f055037f", 4 | "ownerUserId": "18b560d3-b524-4e11-a5cc-9aa46dc31350" 5 | }, 6 | "identity": { 7 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 8 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 9 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 10 | "claims": { 11 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 12 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 13 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 14 | "preferred_username": "testcompany", 15 | "aud": "vagd8sae0kg9sm8l829trej5i", 16 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 17 | "token_use": "id", 18 | "auth_time": 1531319357, 19 | "name": "Dimitri", 20 | "exp": 1531322957, 21 | "iat": 1531319357, 22 | "family_name": "Tarasowski", 23 | "email": "dimitri+test@tarasowski.de" 24 | }, 25 | "sourceIp": [ 26 | "37.221.177.4" 27 | ], 28 | "defaultAuthStrategy": "ALLOW", 29 | "groups": null 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/cognito-create-user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "name", 4 | "Value": "Jan" 5 | }, 6 | { 7 | "Name": "family_name", 8 | "Value": "Reimann" 9 | }, 10 | { 11 | "Name": "preferred_username", 12 | "Value": "testcompany" 13 | } 14 | 15 | ] -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/get-companies-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "entityId": "contact", 4 | }, 5 | "identity": { 6 | "sub": "4e4ea398-c4ac-47a0-b0d6-daadccbea0ee", 7 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 8 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 9 | "claims": { 10 | "sub": "4e4ea398-c4ac-47a0-b0d6-daadccbea0ee", 11 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "preferred_username": "testcompany", 14 | "aud": "vagd8sae0kg9sm8l829trej5i", 15 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 16 | "token_use": "id", 17 | "auth_time": 1531319357, 18 | "name": "Dimitri", 19 | "exp": 1531322957, 20 | "iat": 1531319357, 21 | "family_name": "Tarasowski", 22 | "email": "dimitri+test@tarasowski.de" 23 | }, 24 | "sourceIp": [ 25 | "37.221.177.4" 26 | ], 27 | "defaultAuthStrategy": "ALLOW", 28 | "groups": null 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/get-contacts-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "entity": "contact" 4 | }, 5 | "identity": { 6 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 7 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 8 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 9 | "claims": { 10 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 11 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 13 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 14 | "aud": "vagd8sae0kg9sm8l829trej5i", 15 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 16 | "token_use": "id", 17 | "auth_time": 1531319357, 18 | "name": "Jan", 19 | "exp": 1531322957, 20 | "iat": 1531319357, 21 | "family_name": "Reimann", 22 | "email": "dimitri+jan@tarasowski.de" 23 | }, 24 | "sourceIp": [ 25 | "37.221.177.4" 26 | ], 27 | "defaultAuthStrategy": "ALLOW", 28 | "groups": null 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/invite-user-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "userEmail": "dimitri+invite@tarasowski.de", 4 | "input": { 5 | "canViewEverything": "no", 6 | "canViewOwnerOnly": "no", 7 | "canEditEverything": "no", 8 | "canEditOwnerOnly": "no", 9 | "canImport": "no", 10 | "canExport": "yes", 11 | "canInvite": "yes", 12 | "hasFullAccess": "yes" 13 | } 14 | }, 15 | "identity": { 16 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 17 | "issuer": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_Ez3lS4Q8O", 18 | "username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 19 | "claims": { 20 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 21 | "cognito:groups": [ 22 | "canInvite", 23 | "hasFullAccess" 24 | ], 25 | "iss": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_Ez3lS4Q8O", 26 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 27 | "preferred_username": "testcompany", 28 | "aud": "4ovlg9o3ahg7703d2aadgfojlg", 29 | "event_id": "e70500f4-8b41-11e8-bf46-99a54d69a556", 30 | "token_use": "id", 31 | "auth_time": 1531997580, 32 | "name": "Dimitri", 33 | "exp": 1532001180, 34 | "iat": 1531997580, 35 | "family_name": "Tarasowski", 36 | "email": "dimitri+test@tarasowski.de" 37 | }, 38 | "sourceIp": [ 39 | "37.221.177.4" 40 | ], 41 | "defaultAuthStrategy": "DENY", 42 | "groups": [ 43 | "canInvite", 44 | "hasFullAccess" 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/save-activity-from-entity-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "activity", 5 | "activityName": "Call John Doe", 6 | "activityType": "call", 7 | "assignToEntityId": "company_5b5e7ea6-9118-4ee7-8b0a-5cd4d18b762b" 8 | } 9 | }, 10 | "identity": { 11 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 12 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 13 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 14 | "claims": { 15 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 16 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 17 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 18 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 19 | "aud": "vagd8sae0kg9sm8l829trej5i", 20 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 21 | "token_use": "id", 22 | "auth_time": 1531319357, 23 | "name": "Jan", 24 | "exp": 1531322957, 25 | "iat": 1531319357, 26 | "family_name": "Reimann", 27 | "email": "dimitri+jan@tarasowski.de" 28 | }, 29 | "sourceIp": [ 30 | "37.221.177.4" 31 | ], 32 | "defaultAuthStrategy": "ALLOW", 33 | "groups": null 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/save-activity-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "activity", 5 | "activityName": "Call John Doe", 6 | "activityType": "call" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/save-company-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "company", 5 | "companyName": "Dimitri Tarasowski", 6 | "companyEmail": "dimitri@tarasowski.de" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/save-contact-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "contact", 5 | "contactName": "Dimitri Tarasowski", 6 | "contactEmail": "dimitri@tarasowski.de" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/save-deal-from-entity-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "deal", 5 | "dealName": "Call John Doe", 6 | "dealValue": "1000", 7 | "assignToEntityId": "company_5b5e7ea6-9118-4ee7-8b0a-5cd4d18b762b" 8 | } 9 | }, 10 | "identity": { 11 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 12 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 13 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 14 | "claims": { 15 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 16 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 17 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 18 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 19 | "aud": "vagd8sae0kg9sm8l829trej5i", 20 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 21 | "token_use": "id", 22 | "auth_time": 1531319357, 23 | "name": "Jan", 24 | "exp": 1531322957, 25 | "iat": 1531319357, 26 | "family_name": "Reimann", 27 | "email": "dimitri+jan@tarasowski.de" 28 | }, 29 | "sourceIp": [ 30 | "37.221.177.4" 31 | ], 32 | "defaultAuthStrategy": "ALLOW", 33 | "groups": null 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/backend/_mocks/aws-mock-generator/sample-events/save-deal-payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "deal", 5 | "dealName": "Create New Website", 6 | "dealValue": "1000" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/cognito-create-user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "name", 4 | "Value": "Jan" 5 | }, 6 | { 7 | "Name": "family_name", 8 | "Value": "Reimann" 9 | }, 10 | { 11 | "Name": "preferred_username", 12 | "Value": "testcompany" 13 | } 14 | 15 | ] -------------------------------------------------------------------------------- /src/backend/_mocks/getCompanies.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "entityId": "contact", 4 | }, 5 | "identity": { 6 | "sub": "4e4ea398-c4ac-47a0-b0d6-daadccbea0ee", 7 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 8 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 9 | "claims": { 10 | "sub": "4e4ea398-c4ac-47a0-b0d6-daadccbea0ee", 11 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "preferred_username": "testcompany", 14 | "aud": "vagd8sae0kg9sm8l829trej5i", 15 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 16 | "token_use": "id", 17 | "auth_time": 1531319357, 18 | "name": "Dimitri", 19 | "exp": 1531322957, 20 | "iat": 1531319357, 21 | "family_name": "Tarasowski", 22 | "email": "dimitri+test@tarasowski.de" 23 | }, 24 | "sourceIp": [ 25 | "37.221.177.4" 26 | ], 27 | "defaultAuthStrategy": "ALLOW", 28 | "groups": null 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/backend/_mocks/getContacts.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "entity": "contact" 4 | }, 5 | "identity": { 6 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 7 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 8 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 9 | "claims": { 10 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 11 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 13 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 14 | "aud": "vagd8sae0kg9sm8l829trej5i", 15 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 16 | "token_use": "id", 17 | "auth_time": 1531319357, 18 | "name": "Jan", 19 | "exp": 1531322957, 20 | "iat": 1531319357, 21 | "family_name": "Reimann", 22 | "email": "dimitri+jan@tarasowski.de" 23 | }, 24 | "sourceIp": [ 25 | "37.221.177.4" 26 | ], 27 | "defaultAuthStrategy": "ALLOW", 28 | "groups": null 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/backend/_mocks/inviteUser.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "userEmail": "dimitri+invite@tarasowski.de", 4 | "input": { 5 | "canViewEverything": "no", 6 | "canViewOwnerOnly": "no", 7 | "canEditEverything": "no", 8 | "canEditOwnerOnly": "no", 9 | "canImport": "no", 10 | "canExport": "yes", 11 | "canInvite": "yes", 12 | "hasFullAccess": "yes" 13 | } 14 | }, 15 | "identity": { 16 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 17 | "issuer": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_Ez3lS4Q8O", 18 | "username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 19 | "claims": { 20 | "sub": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 21 | "cognito:groups": [ 22 | "canInvite", 23 | "hasFullAccess" 24 | ], 25 | "iss": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_Ez3lS4Q8O", 26 | "cognito:username": "18b560d3-b524-4e11-a5cc-9aa46dc31350", 27 | "preferred_username": "testcompany", 28 | "aud": "4ovlg9o3ahg7703d2aadgfojlg", 29 | "event_id": "e70500f4-8b41-11e8-bf46-99a54d69a556", 30 | "token_use": "id", 31 | "auth_time": 1531997580, 32 | "name": "Dimitri", 33 | "exp": 1532001180, 34 | "iat": 1531997580, 35 | "family_name": "Tarasowski", 36 | "email": "dimitri+test@tarasowski.de" 37 | }, 38 | "sourceIp": [ 39 | "37.221.177.4" 40 | ], 41 | "defaultAuthStrategy": "DENY", 42 | "groups": [ 43 | "canInvite", 44 | "hasFullAccess" 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /src/backend/_mocks/saveActivity.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "activity", 5 | "activityName": "Call John Doe", 6 | "activityType": "call" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/saveActivityFromEntity.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "activity", 5 | "activityName": "Call John Doe", 6 | "activityType": "call", 7 | "assignToEntityId": "company_5b5e7ea6-9118-4ee7-8b0a-5cd4d18b762b" 8 | } 9 | }, 10 | "identity": { 11 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 12 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 13 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 14 | "claims": { 15 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 16 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 17 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 18 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 19 | "aud": "vagd8sae0kg9sm8l829trej5i", 20 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 21 | "token_use": "id", 22 | "auth_time": 1531319357, 23 | "name": "Jan", 24 | "exp": 1531322957, 25 | "iat": 1531319357, 26 | "family_name": "Reimann", 27 | "email": "dimitri+jan@tarasowski.de" 28 | }, 29 | "sourceIp": [ 30 | "37.221.177.4" 31 | ], 32 | "defaultAuthStrategy": "ALLOW", 33 | "groups": null 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/backend/_mocks/saveCompany.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "company", 5 | "companyName": "Dimitri Tarasowski", 6 | "companyEmail": "dimitri@tarasowski.de" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/saveContact.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "contact", 5 | "contactName": "Dimitri Tarasowski", 6 | "contactEmail": "dimitri@tarasowski.de" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/saveDeal.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "deal", 5 | "dealName": "Create New Website", 6 | "dealValue": "1000" 7 | } 8 | }, 9 | "identity": { 10 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 11 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 12 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 13 | "claims": { 14 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 15 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 16 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 17 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 18 | "aud": "vagd8sae0kg9sm8l829trej5i", 19 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 20 | "token_use": "id", 21 | "auth_time": 1531319357, 22 | "name": "Jan", 23 | "exp": 1531322957, 24 | "iat": 1531319357, 25 | "family_name": "Reimann", 26 | "email": "dimitri+jan@tarasowski.de" 27 | }, 28 | "sourceIp": [ 29 | "37.221.177.4" 30 | ], 31 | "defaultAuthStrategy": "ALLOW", 32 | "groups": null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/backend/_mocks/saveDealFromEntity.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "input": { 4 | "entity": "deal", 5 | "dealName": "Call John Doe", 6 | "dealValue": "1000", 7 | "assignToEntityId": "company_5b5e7ea6-9118-4ee7-8b0a-5cd4d18b762b" 8 | } 9 | }, 10 | "identity": { 11 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 12 | "issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 13 | "username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 14 | "claims": { 15 | "sub": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 16 | "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Vuh0J2ik9", 17 | "cognito:username": "b81eff70-e558-4a0e-bed7-1ce28507e8fd", 18 | "preferred_username": "cac689b4-079b-48ff-bd8d-a45b61da291a", 19 | "aud": "vagd8sae0kg9sm8l829trej5i", 20 | "event_id": "cac1bff3-8516-11e8-a09e-fb9c75a93b8d", 21 | "token_use": "id", 22 | "auth_time": 1531319357, 23 | "name": "Jan", 24 | "exp": 1531322957, 25 | "iat": 1531319357, 26 | "family_name": "Reimann", 27 | "email": "dimitri+jan@tarasowski.de" 28 | }, 29 | "sourceIp": [ 30 | "37.221.177.4" 31 | ], 32 | "defaultAuthStrategy": "ALLOW", 33 | "groups": null 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/backend/_mocks/test.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "source": "arn:aws:dynamodb:us-east-1:059493405231:table/update-streams-test/stream/a804b686-ba0c-3b4f-ba26-af2e5d3e16ea-09223370621958198992-89ff842d/", 3 | "Records": [ 4 | { 5 | "awsRegion": "us-east-1", 6 | "dynamodb": { 7 | "Keys": { 8 | "ForumName": { 9 | "S": "DynamoDB" 10 | }, 11 | "Subject": { 12 | "S": "DynamoDB Thread 3" 13 | } 14 | }, 15 | "NewImage": { 16 | "Attribute_0": { 17 | "S": "New_Value_0" 18 | }, 19 | "Attribute_1": { 20 | "S": "New_Value_1" 21 | }, 22 | "key": { 23 | "S": "key-value" 24 | }, 25 | "range": { 26 | "S": "range-value" 27 | } 28 | }, 29 | "SequenceNumber": "300000000000000499659", 30 | "SizeBytes": 41, 31 | "StreamViewType": "KEYS_ONLY" 32 | }, 33 | "eventID": "e2fd9c34eff2d779b297b26f5fef4206", 34 | "eventName": "INSERT", 35 | "eventSource": "aws:dynamodb", 36 | "eventTime": 1414474536.43, 37 | "eventVersion": "1.0" 38 | }, 39 | { 40 | "awsRegion": "us-east-1", 41 | "dynamodb": { 42 | "Keys": { 43 | "ForumName": { 44 | "S": "DynamoDB" 45 | }, 46 | "Subject": { 47 | "S": "DynamoDB Thread 1" 48 | } 49 | }, 50 | "SequenceNumber": "400000000000000499660", 51 | "SizeBytes": 41, 52 | "StreamViewType": "KEYS_ONLY" 53 | }, 54 | "eventID": "4b25bd0da9a181a155114127e4837252", 55 | "eventName": "MODIFY", 56 | "eventSource": "aws:dynamodb", 57 | "eventTime": 1414474536.43, 58 | "eventVersion": "1.0" 59 | }, 60 | { 61 | "awsRegion": "us-east-1", 62 | "dynamodb": { 63 | "Keys": { 64 | "ForumName": { 65 | "S": "DynamoDB" 66 | }, 67 | "Subject": { 68 | "S": "DynamoDB Thread 2" 69 | } 70 | }, 71 | "SequenceNumber": "500000000000000499661", 72 | "SizeBytes": 41, 73 | "StreamViewType": "KEYS_ONLY" 74 | }, 75 | "eventID": "740280c73a3df7842edab3548a1b08ad", 76 | "eventName": "REMOVE", 77 | "eventSource": "aws:dynamodb", 78 | "eventTime": 1414474536.431, 79 | "eventVersion": "1.0" 80 | } 81 | ] 82 | }; -------------------------------------------------------------------------------- /src/backend/_utils/adapters/cognito-adapter.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk') 2 | const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider() 3 | const config = require('../../_config/config.json') 4 | 5 | 6 | const USER_POOL_ID = process.env.USER_POOL_ID || config.cognitoUserPoolId 7 | 8 | 9 | 10 | module.exports.getCognitoUsers = async(user) => { 11 | const params = { 12 | UserPoolId: USER_POOL_ID, 13 | AttributesToGet: ['email', 'name', 'family_name', 'sub', 'preferred_username'], 14 | Filter: `preferred_username = "${user.companyId}"`, 15 | Limit: 0, 16 | //PaginationToken: 'STRING_VALUE' 17 | }; 18 | 19 | 20 | return cognitoidentityserviceprovider.listUsers(params).promise() 21 | } 22 | 23 | module.exports.createUser = (user, payload) => { 24 | const params = { 25 | UserPoolId: USER_POOL_ID, 26 | Username: payload.userEmailForInvitation, 27 | DesiredDeliveryMediums: ['EMAIL'], 28 | UserAttributes: [ 29 | { 30 | Name: 'preferred_username', 31 | Value: user.companyId 32 | } 33 | ] 34 | } 35 | 36 | return cognitoidentityserviceprovider.adminCreateUser(params).promise() 37 | } 38 | 39 | module.exports.assignUserToGroups = (payload, responseFromCognito) => { 40 | const promises = [] 41 | 42 | 43 | Object.keys(payload.accessRights).forEach(key => { 44 | if (payload.accessRights[key] === 'yes') { 45 | const params = { 46 | GroupName: key, 47 | UserPoolId: USER_POOL_ID, 48 | Username: responseFromCognito.User.Username 49 | } 50 | 51 | promises.push(cognitoidentityserviceprovider.adminAddUserToGroup(params).promise()) 52 | 53 | } 54 | }) 55 | 56 | return promises 57 | 58 | } -------------------------------------------------------------------------------- /src/backend/_utils/adapters/dynamodb-adapter.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk') 2 | const dynamodb = new AWS.DynamoDB() 3 | const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider() 4 | const config = require('../../_config/config.json') 5 | 6 | const TABLE_NAME = process.env.TABLE_NAME || config.dynamodbTableName 7 | const USER_POOL_ID = process.env.USER_POOL_ID || config.cognitoUserPoolId 8 | const createDate = new Date().toISOString() 9 | 10 | module.exports.saveContact = (user, contact) => { 11 | const params = { 12 | TableName: TABLE_NAME, 13 | Item: { 14 | "userId": { 15 | "S": user.id 16 | }, 17 | "entityId": { 18 | "S": contact.entityId 19 | }, 20 | "companyId": { 21 | "S": user.companyId 22 | }, 23 | "name": { 24 | "S": contact.name 25 | }, 26 | "entity": { 27 | "S": contact.entity 28 | }, 29 | "email": { 30 | "S": contact.email 31 | }, 32 | "createDate": { 33 | "S": createDate 34 | } 35 | } 36 | } 37 | 38 | return dynamodb.putItem(params).promise() 39 | } 40 | 41 | module.exports.saveCompany = (user, company) => { 42 | 43 | const params = { 44 | TableName: TABLE_NAME, 45 | Item: { 46 | "userId": { 47 | "S": user.id 48 | }, 49 | "entityId": { 50 | "S": company.entityId 51 | }, 52 | "companyId": { 53 | "S": user.companyId 54 | }, 55 | "name": { 56 | "S": company.name 57 | }, 58 | "entity": { 59 | "S": company.entity 60 | }, 61 | "email": { 62 | "S": company.email 63 | }, 64 | "createDate": { 65 | "S": createDate 66 | } 67 | } 68 | } 69 | 70 | return dynamodb.putItem(params).promise() 71 | } 72 | 73 | module.exports.saveDeal = (user, deal) => { 74 | const params = { 75 | TableName: TABLE_NAME, 76 | Item: { 77 | "userId": { 78 | "S": user.id 79 | }, 80 | "entityId": { 81 | "S": deal.entityId 82 | }, 83 | "companyId": { 84 | "S": user.companyId 85 | }, 86 | "name": { 87 | "S": deal.name 88 | }, 89 | "entity": { 90 | "S": deal.entity 91 | }, 92 | "value": { 93 | "N": String(deal.value) 94 | }, 95 | "createDate": { 96 | "S": createDate 97 | } 98 | } 99 | } 100 | return dynamodb.putItem(params).promise() 101 | } 102 | 103 | module.exports.saveActivity = (user, activity) => { 104 | const params = { 105 | TableName: TABLE_NAME, 106 | Item: { 107 | "userId": { 108 | "S": user.id 109 | }, 110 | "entityId": { 111 | "S": activity.entityId 112 | }, 113 | "companyId": { 114 | "S": user.companyId 115 | }, 116 | "name": { 117 | "S": activity.name 118 | }, 119 | "activityType": { 120 | "S": activity.type 121 | }, 122 | "entity": { 123 | "S": activity.entity 124 | }, 125 | "createDate": { 126 | "S": createDate 127 | } 128 | } 129 | } 130 | 131 | return dynamodb.putItem(params).promise() 132 | } 133 | 134 | module.exports.assignActivityToEntity = async(user, payload) => { 135 | 136 | // Step 1. fetch all activity details from the database 137 | 138 | const fetchActivityDetailsParams = { 139 | TableName: TABLE_NAME, 140 | KeyConditionExpression: 'userId = :val1 AND entityId = :val2', 141 | ExpressionAttributeValues: { 142 | ":val1": { 143 | "S": user.id 144 | }, 145 | ":val2": { 146 | "S": payload.activityEntityId 147 | } 148 | } 149 | } 150 | 151 | let fetchEntity 152 | let fetchCreateDate 153 | let fetchActivityType 154 | let fetchCompanyId 155 | let fetchName 156 | let fetchEntityId 157 | 158 | await dynamodb.query(fetchActivityDetailsParams).promise() 159 | .then(data => { 160 | fetchEntity = data.Items[0].entity.S 161 | fetchActivityType = data.Items[0].activityType.S 162 | fetchCreateDate = data.Items[0].createDate.S 163 | fetchCompanyId = data.Items[0].companyId.S 164 | fetchName = data.Items[0].name.S 165 | fetchEntityId = data.Items[0].entityId.S 166 | }) 167 | .catch(err => console.log(err)) 168 | 169 | // Step 2. assign activity details to an entity in the database 170 | 171 | const activityDetailsParams = { 172 | TableName: TABLE_NAME, 173 | Key: { 174 | "userId": { 175 | "S": user.id 176 | }, 177 | "entityId": { 178 | "S": payload.entityId 179 | } 180 | 181 | }, 182 | UpdateExpression: "SET #AID = list_append(if_not_exists(#AID, :emptyList), :val1), #ADT = list_append(if_not_exists(#ADT, :emptyList), :val2)", 183 | ExpressionAttributeNames: { 184 | "#AID": "activityId", 185 | "#ADT": "activityDetails" 186 | }, 187 | ExpressionAttributeValues: { 188 | ":emptyList": { 189 | "L": [] 190 | }, 191 | ":val1": { 192 | "L":[{"S": payload.activityEntityId}] 193 | }, 194 | ":val2": { "L": [{ 195 | "M": { 196 | "userId": { 197 | "S": user.id 198 | }, 199 | "companyId": { 200 | "S": fetchCompanyId 201 | }, 202 | "name": { 203 | "S": fetchName 204 | }, 205 | "activityType": { 206 | "S": fetchActivityType 207 | }, 208 | "createDate": { 209 | "S": fetchCreateDate 210 | }, 211 | "entity": { 212 | "S": fetchEntity 213 | }, 214 | "entityId": { 215 | "S": fetchEntityId 216 | } 217 | } 218 | }] 219 | } 220 | } 221 | 222 | } 223 | 224 | return dynamodb.updateItem(activityDetailsParams).promise() 225 | } 226 | 227 | module.exports.assignCompanyToEntity = async(user, payload) => { 228 | 229 | // Step 1. fetch company details from the database 230 | 231 | const fetchCompanyDetailsParams = { 232 | TableName: TABLE_NAME, 233 | KeyConditionExpression: 'userId = :val1 AND entityId = :val2', 234 | ExpressionAttributeValues: { 235 | ":val1": { 236 | "S": user.id 237 | }, 238 | ":val2": { 239 | "S": payload.companyEntityId 240 | } 241 | } 242 | } 243 | 244 | let fetchEntity 245 | let fetchCreateDate 246 | let fetchEmail 247 | let fetchCognitoCompanyId 248 | let fetchName 249 | let fetchEntityId 250 | 251 | await dynamodb.query(fetchCompanyDetailsParams).promise() 252 | .then(data => { 253 | fetchEntity = data.Items[0].entity.S 254 | fetchEmail = data.Items[0].email.S 255 | fetchCreateDate = data.Items[0].createDate.S 256 | fetchCognitoCompanyId = data.Items[0].companyId.S 257 | fetchName = data.Items[0].name.S 258 | fetchEntityId = data.Items[0].entityId.S 259 | }) 260 | .catch(err => console.log(err)) 261 | 262 | // Step 2. assign activity details to an entity in the database 263 | 264 | const dealDetailsParams = { 265 | TableName: TABLE_NAME, 266 | Key: { 267 | "userId": { 268 | "S": user.id 269 | }, 270 | "entityId": { 271 | "S": payload.entityId 272 | } 273 | 274 | }, 275 | UpdateExpression: "SET #ACID = :val1, #ACDT = :val2", 276 | ExpressionAttributeNames: { 277 | "#ACID": "associatedCompanyId", 278 | "#ACDT": "associatedCompanyDetails" 279 | }, 280 | ExpressionAttributeValues: { 281 | ":val1": { 282 | "S": fetchEntityId 283 | }, 284 | ":val2": { 285 | "M": { 286 | "userId": { 287 | "S": user.id 288 | }, 289 | "companyId": { 290 | "S": fetchCognitoCompanyId 291 | }, 292 | "name": { 293 | "S": fetchName 294 | }, 295 | "email": { 296 | "S": fetchEmail 297 | }, 298 | "createDate": { 299 | "S": fetchCreateDate 300 | }, 301 | "entity": { 302 | "S": fetchEntity 303 | }, 304 | "entityId": { 305 | "S": fetchEntityId 306 | } 307 | } 308 | } 309 | } 310 | 311 | } 312 | 313 | return dynamodb.updateItem(dealDetailsParams).promise() 314 | } 315 | 316 | module.exports.assignContactToEntity = async(user, payload) => { 317 | 318 | const fetchParams = { 319 | TableName: TABLE_NAME, 320 | KeyConditionExpression: 'userId = :val1 AND entityId = :val2', 321 | ExpressionAttributeValues: { 322 | ":val1": { 323 | "S": user.id 324 | }, 325 | ":val2": { 326 | "S": payload.contactEntityId 327 | } 328 | } 329 | } 330 | 331 | let fetchEntity 332 | let fetchCreateDate 333 | let fetchCognitoCompanyId 334 | let fetchName 335 | let fetchEmail 336 | let fetchEntityId 337 | 338 | await dynamodb.query(fetchParams).promise() 339 | .then(data => { 340 | fetchEntity = data.Items[0].entity.S 341 | fetchCreateDate = data.Items[0].createDate.S 342 | fetchCognitoCompanyId = data.Items[0].companyId.S 343 | fetchName = data.Items[0].name.S 344 | fetchEmail = data.Items[0].email.S 345 | fetchEntityId = data.Items[0].entityId.S 346 | }) 347 | .catch(err => console.log(err)) 348 | 349 | 350 | const params = { 351 | TableName: TABLE_NAME, 352 | Key: { 353 | "userId": { 354 | "S": user.id 355 | }, 356 | "entityId": { 357 | "S": payload.entityId 358 | } 359 | 360 | }, 361 | UpdateExpression: "SET #CID = list_append(if_not_exists(#CID, :emptyList), :val1), #CDT = list_append(if_not_exists(#CDT, :emptyList), :val2)", 362 | ExpressionAttributeNames: { 363 | "#CID": "contactId", 364 | "#CDT": "contactDetails" 365 | }, 366 | ExpressionAttributeValues: { 367 | ":emptyList": { 368 | "L": [] 369 | }, 370 | ":val1": { 371 | "L": [{"S": payload.contactEntityId}] 372 | }, 373 | ":val2": { 374 | "L": [{ 375 | "M": { 376 | "userId": { 377 | "S": user.id 378 | }, 379 | "companyId": { 380 | "S": fetchCognitoCompanyId 381 | }, 382 | "createDate": { 383 | "S": fetchCreateDate 384 | }, 385 | "name": { 386 | "S": fetchName 387 | }, 388 | "entityId": { 389 | "S": fetchEntityId 390 | }, 391 | "entity": { 392 | "S": fetchEntity 393 | }, 394 | "email": { 395 | "S": fetchEmail 396 | } 397 | } 398 | }] 399 | } 400 | } 401 | 402 | } 403 | 404 | return dynamodb.updateItem(params).promise() 405 | } 406 | 407 | module.exports.assignDealToEntity = async(user, payload) => { 408 | const fetchParams = { 409 | TableName: TABLE_NAME, 410 | KeyConditionExpression: 'userId = :val1 AND entityId = :val2', 411 | ExpressionAttributeValues: { 412 | ":val1": { 413 | "S": user.id 414 | }, 415 | ":val2": { 416 | "S": payload.dealEntityId 417 | } 418 | } 419 | } 420 | 421 | let fetchEntity 422 | let fetchValue 423 | let fetchCreateDate 424 | let fetchCognitoCompanyId 425 | let fetchName 426 | let fetchEntityId 427 | 428 | await dynamodb.query(fetchParams).promise() 429 | .then(data => { 430 | fetchEntity = data.Items[0].entity.S 431 | fetchValue = data.Items[0].value.N 432 | fetchCreateDate = data.Items[0].createDate.S 433 | fetchCognitoCompanyId = data.Items[0].companyId.S 434 | fetchName = data.Items[0].name.S 435 | fetchEntityId = data.Items[0].entityId.S 436 | }) 437 | .catch(err => console.log(err)) 438 | 439 | const params = { 440 | TableName: TABLE_NAME, 441 | Key: { 442 | "userId": { 443 | "S": user.id 444 | }, 445 | "entityId": { 446 | "S": payload.entityId 447 | } 448 | 449 | }, 450 | UpdateExpression: "SET #DID = list_append(if_not_exists(#DID, :emptyList), :val1), #DDT = list_append(if_not_exists(#DDT, :emptyList), :val2)", 451 | ExpressionAttributeNames: { 452 | "#DID": "dealId", 453 | "#DDT": "dealDetails" 454 | }, 455 | ExpressionAttributeValues: { 456 | ":emptyList": { 457 | "L": [] 458 | }, 459 | ":val1": { 460 | "L": [{"S": payload.dealEntityId}] 461 | }, 462 | ":val2": { "L": [{ 463 | "M": { 464 | "userId": { 465 | "S": user.id 466 | }, 467 | "companyId": { 468 | "S": fetchCognitoCompanyId 469 | }, 470 | "value": { 471 | "S": fetchValue 472 | }, 473 | "createDate": { 474 | "S": fetchCreateDate 475 | }, 476 | "name": { 477 | "S": fetchName 478 | }, 479 | "entityId": { 480 | "S": fetchEntityId 481 | }, 482 | "entity": { 483 | "S": fetchEntity 484 | } 485 | } 486 | }] 487 | } 488 | } 489 | 490 | } 491 | 492 | return dynamodb.updateItem(params).promise() 493 | 494 | } 495 | 496 | module.exports.assignOwnerToEntity = async(user, payload) => { 497 | const cognitoParams = { 498 | UserPoolId: USER_POOL_ID, 499 | AttributesToGet: ['email', 'name', 'family_name', 'sub', 'preferred_username'], 500 | Filter: `sub = "${payload.ownerId}"`, 501 | Limit: 0, 502 | //PaginationToken: 'STRING_VALUE' 503 | } 504 | 505 | 506 | const userDetails = await cognitoidentityserviceprovider.listUsers(cognitoParams).promise() 507 | .then(data => { 508 | 509 | return data.Users.map(element => { 510 | return { 511 | userId: element.Attributes[0].Value, 512 | firstName: element.Attributes[1].Value, 513 | lastName: element.Attributes[3].Value, 514 | companyId: element.Attributes[2].Value, 515 | email: element.Attributes[4].Value 516 | } 517 | }) 518 | }) 519 | .catch(err => console.log(err)) 520 | 521 | const params = { 522 | TableName: TABLE_NAME, 523 | Key: { 524 | "userId": { 525 | "S": user.id 526 | }, 527 | "entityId": { 528 | "S": payload.entityId 529 | } 530 | 531 | }, 532 | UpdateExpression: "SET #OID = :val1, #ODT = :val2", 533 | ExpressionAttributeNames: { 534 | "#OID": "ownerId", 535 | "#ODT": "ownerDetails" 536 | }, 537 | ExpressionAttributeValues: { 538 | ":val1": { 539 | "S": userDetails[0].userId 540 | }, 541 | ":val2": { 542 | "M": { 543 | "userId": { 544 | "S": userDetails[0].userId 545 | }, 546 | "companyId": { 547 | "S": userDetails[0].companyId 548 | }, 549 | "firstName": { 550 | "S": userDetails[0].firstName 551 | }, 552 | "lastName": { 553 | "S": userDetails[0].lastName 554 | }, 555 | "email": { 556 | "S": userDetails[0].email 557 | } 558 | } 559 | } 560 | } 561 | 562 | } 563 | 564 | return dynamodb.updateItem(params).promise() 565 | } 566 | 567 | module.exports.saveActivityFromEntity = async(user, payload) => { 568 | const params = { 569 | TableName: TABLE_NAME, 570 | ReturnConsumedCapacity: "TOTAL", 571 | Item: { 572 | "userId": { 573 | "S": user.id 574 | }, 575 | "entityId": { 576 | "S": payload.entityId 577 | }, 578 | "companyId": { 579 | "S": user.companyId 580 | }, 581 | "name": { 582 | "S": payload.activityName 583 | }, 584 | "activityType": { 585 | "S": payload.activityType 586 | }, 587 | "entity": { 588 | "S": payload.entity 589 | }, 590 | "createDate": { 591 | "S": createDate 592 | } 593 | } 594 | } 595 | 596 | await dynamodb.putItem(params).promise() 597 | .then(() => console.log('activity saved to database')) 598 | .catch(err => console.log(err)) 599 | 600 | const updateParams = { 601 | TableName: TABLE_NAME, 602 | Key: { 603 | "userId": { 604 | "S": user.id 605 | }, 606 | "entityId": { 607 | "S": payload.assignToEntityId 608 | } 609 | 610 | }, 611 | UpdateExpression: "SET #AID = list_append(if_not_exists(#AID, :emptyList), :val1), #ADT = list_append(if_not_exists(#ADT, :emptyList), :val2)", 612 | ExpressionAttributeNames: { 613 | "#AID": "activityId", 614 | "#ADT": "activityDetails" 615 | }, 616 | ExpressionAttributeValues: { 617 | ":emptyList": { 618 | "L": [] 619 | }, 620 | ":val1": { 621 | "L": [ {"S": payload.entityId} ] 622 | }, 623 | ":val2": { 624 | "L": [ 625 | { "M": { 626 | "userId": { 627 | "S": user.id 628 | }, 629 | "entityId": { 630 | "S": payload.entityId 631 | }, 632 | "companyId": { 633 | "S": user.companyId 634 | }, 635 | "name": { 636 | "S": payload.activityName 637 | }, 638 | "activityType": { 639 | "S": payload.activityType 640 | }, 641 | "entity": { 642 | "S": payload.entity 643 | }, 644 | "createDate": { 645 | "S": createDate 646 | } 647 | 648 | } 649 | }] 650 | } 651 | } 652 | 653 | } 654 | 655 | return dynamodb.updateItem(updateParams).promise() 656 | 657 | } 658 | 659 | module.exports.saveDealFromEntity = async(user, payload) => { 660 | 661 | const params = { 662 | TableName: TABLE_NAME, 663 | ReturnConsumedCapacity: "TOTAL", 664 | Item: { 665 | "userId": { 666 | "S": user.id 667 | }, 668 | "entityId": { 669 | "S": payload.entityId 670 | }, 671 | "companyId": { 672 | "S": user.companyId 673 | }, 674 | "name": { 675 | "S": payload.dealName 676 | }, 677 | "entity": { 678 | "S": payload.entity 679 | }, 680 | "value": { 681 | "N": payload.dealValue 682 | }, 683 | "createDate": { 684 | "S": createDate 685 | } 686 | } 687 | } 688 | 689 | await dynamodb.putItem(params).promise() 690 | .then(() => `Saved successfully to your database`) 691 | .catch(err => console.log(err)) 692 | 693 | 694 | 695 | const updateParams = { 696 | TableName: TABLE_NAME, 697 | Key: { 698 | "userId": { 699 | "S": user.id 700 | }, 701 | "entityId": { 702 | "S": payload.assignToEntityId 703 | } 704 | 705 | }, 706 | UpdateExpression: "SET #DID = list_append(if_not_exists(#DID, :emptyList), :val1), #DDT = list_append(if_not_exists(#DDT, :emptyList), :val2)", 707 | ExpressionAttributeNames: { 708 | "#DID": "dealId", 709 | "#DDT": "dealDetails" 710 | }, 711 | ExpressionAttributeValues: { 712 | ":emptyList": { 713 | "L": [] 714 | }, 715 | ":val1": { 716 | "L": [ {"S": payload.entityId} ] 717 | }, 718 | ":val2": { 719 | "L": [ 720 | { "M": { 721 | "userId": { 722 | "S": user.id 723 | }, 724 | "entityId": { 725 | "S": payload.entityId 726 | }, 727 | "companyId": { 728 | "S": user.companyId 729 | }, 730 | "name": { 731 | "S": payload.dealName 732 | }, 733 | "value": { 734 | "N": payload.dealValue 735 | }, 736 | "entity": { 737 | "S": payload.entity 738 | }, 739 | "createDate": { 740 | "S": createDate 741 | } 742 | 743 | } 744 | }] 745 | } 746 | } 747 | 748 | } 749 | return dynamodb.updateItem(updateParams).promise() 750 | 751 | } 752 | 753 | module.exports.getContacts = (user, payload) => { 754 | const params = { 755 | TableName: TABLE_NAME, 756 | ExpressionAttributeValues: { 757 | ":v1": { 758 | "S": user.id 759 | }, 760 | ":v2": { 761 | "S": payload.entity 762 | } 763 | }, 764 | KeyConditionExpression: "userId = :v1 AND begins_with ( entityId, :v2 ) ", 765 | //ReturnConsumedCapacity: "TOTAL" 766 | } 767 | 768 | return dynamodb.query(params).promise() 769 | } 770 | 771 | module.exports.getDeals = async(user, payload) => { 772 | const params = { 773 | TableName: TABLE_NAME, 774 | ExpressionAttributeValues: { 775 | ":v1": { 776 | "S": user.id 777 | }, 778 | ":v2": { 779 | "S": payload.entity 780 | } 781 | }, 782 | KeyConditionExpression: "userId = :v1 AND begins_with ( entityId, :v2 ) ", 783 | ReturnConsumedCapacity: "TOTAL" 784 | } 785 | return dynamodb.query(params).promise() 786 | } 787 | 788 | module.exports.getActivities = async(user, payload) => { 789 | const params = { 790 | TableName: TABLE_NAME, 791 | ExpressionAttributeValues: { 792 | ":v1": { 793 | "S": user.id 794 | }, 795 | ":v2": { 796 | "S": payload.entity 797 | } 798 | }, 799 | KeyConditionExpression: "userId = :v1 AND begins_with ( entityId, :v2 ) ", 800 | ReturnConsumedCapacity: "TOTAL" 801 | } 802 | 803 | return dynamodb.query(params).promise() 804 | } 805 | 806 | module.exports.getCompanies = async(user, payload) => { 807 | const params = { 808 | TableName: TABLE_NAME, 809 | ExpressionAttributeValues: { 810 | ":v1": { 811 | "S": user.id 812 | }, 813 | ":v2": { 814 | "S": payload.entity 815 | } 816 | }, 817 | KeyConditionExpression: "userId = :v1 AND begins_with ( entityId, :v2 ) ", 818 | ReturnConsumedCapacity: "TOTAL" 819 | } 820 | 821 | return dynamodb.query(params).promise() 822 | } 823 | 824 | module.exports.getCognitoUsers = async(user) => { 825 | const params = { 826 | UserPoolId: USER_POOL_ID, 827 | AttributesToGet: ['email', 'name', 'family_name', 'sub', 'preferred_username'], 828 | Filter: `preferred_username = "${user.companyId}"`, 829 | Limit: 0, 830 | //PaginationToken: 'STRING_VALUE' 831 | }; 832 | 833 | 834 | return cognitoidentityserviceprovider.listUsers(params).promise() 835 | } -------------------------------------------------------------------------------- /src/backend/_utils/parse/parse-request-arguments.js: -------------------------------------------------------------------------------- 1 | const uuidv4 = require('uuid/v4') 2 | 3 | module.exports.saveContact = (event) => { 4 | const input = event.args['input'] 5 | const entity = input.entity 6 | const entityId = input.entity + '_' + uuidv4() 7 | const name = input.contactName 8 | const email = input.contactEmail 9 | 10 | return { 11 | entity, 12 | entityId, 13 | name, 14 | email 15 | } 16 | 17 | } 18 | 19 | module.exports.saveCompany = (event) => { 20 | const input = event.args['input'] 21 | const entity = input.entity 22 | const entityId = input.entity + '_' + uuidv4() 23 | const name = input.companyName 24 | const email = input.companyEmail 25 | 26 | return { 27 | entity, 28 | entityId, 29 | name, 30 | email 31 | } 32 | 33 | } 34 | 35 | module.exports.saveDeal = (event) => { 36 | const input = event.args['input'] 37 | const entity = input.entity 38 | const entityId = input.entity + '_' + uuidv4() 39 | const name = input.dealName 40 | const value = input.dealValue 41 | 42 | return { 43 | entity, 44 | entityId, 45 | name, 46 | value 47 | } 48 | 49 | } 50 | 51 | module.exports.saveActivity = (event) => { 52 | const input = event.args['input'] 53 | const entity = input.entity 54 | const entityId = input.entity + '_' + uuidv4() 55 | const name = input.activityName 56 | const type = input.activityType 57 | 58 | return { 59 | entity, 60 | entityId, 61 | name, 62 | type 63 | } 64 | 65 | } 66 | 67 | module.exports.assignActivityToEntity = (event) => { 68 | const entityId = event.args.entityId 69 | const activityEntityId = event.args.activityEntityId 70 | 71 | return { 72 | entityId, 73 | activityEntityId 74 | } 75 | } 76 | 77 | module.exports.assignCompanyToEntity = (event) => { 78 | const entityId = event.args.entityId 79 | const companyEntityId = event.args.companyEntityId 80 | 81 | return { 82 | entityId, 83 | companyEntityId 84 | } 85 | } 86 | 87 | module.exports.assignContactToEntity = (event) => { 88 | const entityId = event.args.entityId 89 | const contactEntityId = event.args.contactEntityId 90 | 91 | return { 92 | entityId, 93 | contactEntityId 94 | } 95 | } 96 | 97 | module.exports.assignDealToEntity = (event) => { 98 | const entityId = event.args.entityId 99 | const dealEntityId = event.args.dealEntityId 100 | 101 | return { 102 | entityId, 103 | dealEntityId 104 | } 105 | } 106 | 107 | module.exports.assignOwnerToEntity = (event) => { 108 | const entityId = event.args.entityId 109 | const ownerId = event.args.ownerUserId 110 | 111 | return { 112 | entityId, 113 | ownerId 114 | } 115 | } 116 | 117 | module.exports.saveActivityFromEntity = (event) => { 118 | const activityName = event.args['input'].activityName 119 | const activityType = event.args['input'].activityType 120 | const entity = event.args['input'].entity 121 | const assignToEntityId = event.args['input'].assignToEntityId 122 | const entityId = entity + '_' + uuidv4() 123 | 124 | return { 125 | activityName, 126 | activityType, 127 | entity, 128 | assignToEntityId, 129 | entityId 130 | } 131 | } 132 | 133 | module.exports.saveDealFromEntity = (event) => { 134 | const entity = event.args['input'].entity 135 | const assignToEntityId = event.args['input'].assignToEntityId 136 | const dealName = event.args['input'].dealName 137 | const dealValue = event.args['input'].dealValue.toString() 138 | const entityId = entity + '_' + uuidv4() 139 | 140 | return { 141 | entity, 142 | assignToEntityId, 143 | dealName, 144 | dealValue, 145 | entityId 146 | } 147 | } 148 | 149 | module.exports.get = (event) => { 150 | const entity = event.args.entity 151 | return { 152 | entity 153 | } 154 | } 155 | 156 | module.exports.inviteUser = (event) => { 157 | 158 | const userEmailForInvitation = event.args.userEmail 159 | const input = event.args['input'] 160 | const canViewEverything = input.canViewEverything 161 | const canViewOwnerOnly = input.canEditOwnerOnly 162 | const canEditEverything = input.canEditEverything 163 | const canEditOwnerOnly = input.canEditOwnerOnly 164 | const canExport = input.canExport 165 | const canImport = input.canImport 166 | const canInvite = input.canInvite 167 | const hasFullAccess = input.hasFullAccess 168 | 169 | return { 170 | userEmailForInvitation, 171 | accessRights: {canViewEverything, 172 | canViewOwnerOnly, 173 | canEditEverything, 174 | canEditOwnerOnly, 175 | canExport, 176 | canImport, 177 | canInvite, 178 | hasFullAccess} 179 | } 180 | } -------------------------------------------------------------------------------- /src/backend/_utils/parse/parse-user-identity.js: -------------------------------------------------------------------------------- 1 | module.exports = (event) => { 2 | 3 | const identity = event.identity.claims 4 | const id = identity.sub 5 | const companyId = identity.preferred_username 6 | const groups = event.groups || 'user is not assiged to a group' 7 | 8 | return { 9 | id, 10 | companyId, 11 | groups 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/backend/_utils/responses/process-response.js: -------------------------------------------------------------------------------- 1 | const responseConstructor = require('./response-constructor') 2 | 3 | module.exports.getContacts = (data) => { 4 | return data.Items.map(responseConstructor.getContacts) 5 | } 6 | 7 | module.exports.getDeals = (data) => { 8 | return data.Items.map(responseConstructor.getDeals) 9 | } 10 | 11 | module.exports.getActivities = (data) => { 12 | return data.Items.map(responseConstructor.getActivities) 13 | } 14 | 15 | module.exports.getCompanies = (data) => { 16 | return data.Items.map(responseConstructor.getCompanies) 17 | } 18 | 19 | module.exports.gotError = (error) => { 20 | console.log(error) 21 | console.log(error.stack) 22 | return error.message 23 | } 24 | 25 | module.exports.getCognitoUsers = (data) => { 26 | return data.Users.map(responseConstructor.getCognitoUsers) 27 | } 28 | 29 | 30 | module.exports.save = (message, data = false) => { 31 | if (!data) return `${message}` 32 | else return `${message}` + data 33 | } 34 | -------------------------------------------------------------------------------- /src/backend/_utils/responses/response-constructor.js: -------------------------------------------------------------------------------- 1 | 2 | const transformList = (entry) => { 3 | if(entry !== undefined && Object.keys(entry).length !== 0 && entry.constructor === Object) { 4 | const list = entry.L.map(element => element.S) 5 | return list 6 | } 7 | return [] 8 | } 9 | 10 | const transformString = (entry) => { 11 | 12 | if(entry !== undefined && Object.keys(entry).length !== 0 && entry.constructor === Object) { 13 | const string = entry.S 14 | return string 15 | } 16 | return '' 17 | } 18 | 19 | const transformMap = (entry) => { 20 | if(entry !== undefined && Object.keys(entry).length !== 0 && entry.constructor === Object) { 21 | 22 | const details = entry.M 23 | const transformedMap = {} 24 | 25 | Object.keys(details).forEach(key => { 26 | transformedMap[key] = details[key].S 27 | }) 28 | 29 | return transformedMap 30 | } 31 | return {} 32 | } 33 | 34 | const transformListMap = (entry) => { 35 | if(entry !== undefined && Object.keys(entry).length !== 0 && entry.constructor === Object) { 36 | 37 | const list = entry.L 38 | 39 | return list.map(element => { 40 | const details = element.M 41 | const transformedMap = {} 42 | 43 | Object.keys(details).forEach(key => { 44 | transformedMap[key] = details[key].S 45 | }) 46 | return transformedMap 47 | 48 | }) 49 | 50 | 51 | 52 | } 53 | 54 | return [] 55 | } 56 | 57 | module.exports.getContacts = (element) => { 58 | if (element) { 59 | return { 60 | userId: element.userId.S, 61 | entityId: element.entityId.S, 62 | companyId: element.companyId.S, 63 | name: element.name.S, 64 | email: element.email.S, 65 | entity: element.entity.S, 66 | createDate: element.createDate.S, 67 | ownerId: transformString(element.ownerId), 68 | ownerDetails: transformMap(element.ownerDetails), 69 | dealId: transformList(element.dealId), 70 | dealDetails: transformListMap(element.dealDetails), 71 | activityId: transformList(element.activityId), 72 | activityDetails: transformListMap(element.activityDetails), 73 | associatedCompanyId: transformString(element.associatedCompanyId), 74 | associatedCompanyDetails: transformMap(element.associatedCompanyDetails), 75 | } 76 | } 77 | 78 | return [] 79 | } 80 | 81 | module.exports.getActivities = (element) => { 82 | if (element) { 83 | return { 84 | userId: element.userId.S, 85 | entityId: element.entityId.S, 86 | companyId: element.companyId.S, 87 | name: element.name.S, 88 | entity: element.entity.S, 89 | createDate: element.createDate.S, 90 | activityType: element.activityType.S 91 | } 92 | } 93 | 94 | return {} 95 | } 96 | 97 | module.exports.getDeals = (element) => { 98 | if (element) { 99 | return { 100 | userId: element.userId.S, 101 | entityId: element.entityId.S, 102 | companyId: element.companyId.S, 103 | name: element.name.S, 104 | value: element.value.N, 105 | entity: element.entity.S, 106 | createDate: element.createDate.S 107 | 108 | } 109 | } 110 | 111 | return {} 112 | } 113 | 114 | module.exports.getCompanies = (element) => { 115 | if (element) { 116 | return { 117 | userId: element.userId.S, 118 | entityId: element.entityId.S, 119 | companyId: element.companyId.S, 120 | name: element.name.S, 121 | email: element.email.S, 122 | entity: element.entity.S, 123 | createDate: element.createDate.S 124 | 125 | } 126 | } 127 | 128 | return {} 129 | } 130 | 131 | module.exports.getCognitoUsers = (element) => { 132 | if (element) { 133 | // checkout the index positions - code smell 134 | return { 135 | userId: element.Attributes[0].Value, 136 | firstName: element.Attributes[1].Value, 137 | lastName: element.Attributes[3].Value, 138 | companyId: element.Attributes[2].Value, 139 | email: element.Attributes[4].Value 140 | } 141 | } 142 | return {} 143 | } -------------------------------------------------------------------------------- /src/backend/_utils/tracker/invocation-tracker.js: -------------------------------------------------------------------------------- 1 | module.exports = (userId, companyId, resourceName, namespace) => { 2 | console.log(`MONITORING|{"userId": "${userId}", "companyId": "${companyId}", "resourceName": "${resourceName}", "namespace": "${namespace}"}|`) 3 | } 4 | -------------------------------------------------------------------------------- /src/backend/activities/assign-activity-to-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').assignActivityToEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.assignActivityToEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/activities/get-activities.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').get 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | exports.handler = async (event) => { 8 | const user = parseUserIdentity(event) 9 | const payload = parseRequestArguments(event) 10 | 11 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 12 | 13 | return db.getActivities(user, payload) 14 | .then(data => processResponse.getActivities(data)) 15 | .catch(err => processResponse.gotError(err)) 16 | } 17 | -------------------------------------------------------------------------------- /src/backend/activities/save-activity-from-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').saveActivityFromEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.saveActivityFromEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/activities/save-activity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').saveActivity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.saveActivity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/companies/assign-company-to-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').assignCompanyToEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.assignCompanyToEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/companies/get-companies.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').get 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | exports.handler = async (event) => { 8 | const user = parseUserIdentity(event) 9 | const payload = parseRequestArguments(event) 10 | 11 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 12 | 13 | return db.getCompanies(user, payload) 14 | .then(data => processResponse.getCompanies(data)) 15 | .catch(err => processResponse.gotError(err)) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/companies/save-company.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').saveCompany 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async(event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.saveCompany(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/contacts/assign-contact-to-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').assignContactToEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.assignContactToEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/contacts/get-contacts.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').get 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | exports.handler = async(event) => { 8 | const user = parseUserIdentity(event) 9 | const payload = parseRequestArguments(event) 10 | 11 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 12 | 13 | return db.getContacts(user, payload) 14 | .then(data => processResponse.getContacts(data)) 15 | .catch(err => processResponse.gotError(err)) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/contacts/save-contact.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').saveContact 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async(event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.saveContact(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/deals/assign-deal-to-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').assignDealToEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.assignDealToEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/deals/get-deals.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').get 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.getDeals(user, payload) 15 | .then(data => processResponse.getDeals(data)) 16 | .catch(err => processResponse.gotError(err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/deals/save-deal-from-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').saveDealFromEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | console.log(payload) 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.saveDealFromEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /src/backend/deals/save-deal.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').saveDeal 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async(event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.saveDeal(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/test/unit/activities.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const proxyquire = require('proxyquire').noCallThru() 3 | const CreateEvent = require('../../_mocks/aws-mock-generator/index') 4 | 5 | test('saveActivity()', async(assert) => { 6 | assert.plan(1) 7 | 8 | const params = { 9 | templateName: 'aws:appsync', 10 | cognitoIdentity: true, 11 | customInput: { 12 | activityName: 'Call Sergey Brin', 13 | activityType: 'todo', 14 | entity: 'activity' 15 | } 16 | } 17 | 18 | const event = CreateEvent(params) 19 | 20 | const dbStub = { 21 | saveActivity: (user, payload) => { 22 | return Promise.resolve('Works') 23 | } 24 | } 25 | 26 | const saveActivity = proxyquire('../../activities/save-activity', {'../_utils/adapters/dynamodb-adapter': dbStub }) 27 | const result = await saveActivity.handler(event) 28 | assert.equal(result, 'Successfully saved to database', 'should return a message in case of success') 29 | 30 | }) -------------------------------------------------------------------------------- /src/backend/test/unit/utils.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const proxyquire = require('proxyquire').noCallThru() 3 | const CreateEvent = require('../../_mocks/aws-mock-generator/index') 4 | const parseUserIdentity = require('../../_utils/parse/parse-user-identity') 5 | const parseRequestArguments = require('../../_utils/parse/parse-request-arguments') 6 | 7 | test('parseUserIdentity() with an event object', (assert) => { 8 | assert.plan(1) 9 | 10 | const params = { 11 | templateName: 'aws:appsync', 12 | cognitoIdentity: true, 13 | customInput: { 14 | contactName: 'Dimitri Tarasowski', 15 | contactEmail: 'dimitri@tarasowski.de', 16 | entity: 'contact' 17 | } 18 | } 19 | 20 | const event = CreateEvent(params) 21 | 22 | 23 | assert.deepEqual(Object.keys(parseUserIdentity(event)).length, 3, 'function should return an object with exactly 3 properties') 24 | }) 25 | 26 | test('parseUserIdentity() lookup property names', (assert) => { 27 | assert.plan(3) 28 | 29 | const params = { 30 | templateName: 'aws:appsync', 31 | cognitoIdentity: true, 32 | customInput: { 33 | contactName: 'Dimitri Tarasowski', 34 | contactEmail: 'dimitri@tarasowski.de', 35 | entity: 'contact' 36 | } 37 | } 38 | 39 | const event = CreateEvent(params) 40 | 41 | const user = parseUserIdentity(event) 42 | const objMustHaveProperties = ['id', 'companyId', 'groups'] 43 | objMustHaveProperties.forEach(element => { 44 | if (user[element]) { 45 | assert.ok(true, `property ${element.toUpperCase()} is on the user object available`) 46 | } else { 47 | assert.ok(false, `something is wrong with: ${element.toUpperCase()} property on the user object`) 48 | } 49 | 50 | }) 51 | }) 52 | 53 | test('parseUserIdentity() with a missing event object', (assert) => { 54 | assert.plan(1) 55 | 56 | const parseUserIdentity = require('../../_utils/parse/parse-user-identity') 57 | const user = parseUserIdentity() 58 | assert.deepEqual(user, [], 'should return an empty array if no event object provided') 59 | 60 | }) 61 | 62 | test('parseRequestArguments.saveContact() with an event object', (assert) => { 63 | assert.plan(1) 64 | 65 | const params = { 66 | templateName: 'aws:appsync', 67 | cognitoIdentity: true, 68 | customInput: { 69 | contactName: 'Dimitri Tarasowski', 70 | contactEmail: 'dimitri@tarasowski.de', 71 | entity: 'contact' 72 | } 73 | } 74 | 75 | const event = CreateEvent(params) 76 | 77 | assert.deepEqual(Object.keys(parseRequestArguments.saveContact(event)).length, 4, 'should return an object with exactly 4 properties') 78 | }) 79 | 80 | test('parseRequestArguments().inviteUsers', (assert) => { 81 | 82 | //assert.deepEqual(Object.keys(parseRequestArguments.inviteUser(payload)).length, 1, 'function should return an object with exactly 1 properties') 83 | assert.end() 84 | }) -------------------------------------------------------------------------------- /src/backend/users/assign-owner-to-entity.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').assignOwnerToEntity 3 | const db = require('../_utils/adapters/dynamodb-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | const user = parseUserIdentity(event) 10 | const payload = parseRequestArguments(event) 11 | 12 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 13 | 14 | return db.assignOwnerToEntity(user, payload) 15 | .then(data => processResponse.save('Successfully saved to db', data)) 16 | .catch(err => processResponse.gotError('Something went wrong', err)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/backend/users/get-cognito-user-list.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const sendMetric = require('../_utils/tracker/invocation-tracker') 3 | const cognito = require('../_utils/adapters/cognito-adapter') 4 | const processResponse = require('../_utils/responses/process-response') 5 | 6 | exports.handler = async (event) => { 7 | const user = parseUserIdentity(event) 8 | 9 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 10 | 11 | return cognito.getCognitoUsers(user) 12 | .then(data => processResponse.getCognitoUsers(data)) 13 | .catch(err => processResponse.gotError(err)) 14 | } 15 | -------------------------------------------------------------------------------- /src/backend/users/invite-user.js: -------------------------------------------------------------------------------- 1 | const parseUserIdentity = require('../_utils/parse/parse-user-identity') 2 | const parseRequestArguments = require('../_utils/parse/parse-request-arguments').inviteUser 3 | const cognito = require('../_utils/adapters/cognito-adapter') 4 | const sendMetric = require('../_utils/tracker/invocation-tracker') 5 | const processResponse = require('../_utils/responses/process-response') 6 | 7 | 8 | exports.handler = async (event) => { 9 | try { 10 | const user = parseUserIdentity(event) 11 | const payload = parseRequestArguments(event) 12 | 13 | sendMetric(user.id, user.companyId, 'lambda', 'claudiacrm') 14 | 15 | const responseFromCognito = await cognito.createUser(user, payload) 16 | const promises = cognito.assignUserToGroups(payload, responseFromCognito) 17 | await Promise.all(promises) 18 | 19 | return processResponse.save('Congratulations! Invitation was successfully sent') 20 | 21 | } catch (error) { 22 | 23 | return processResponse.gotError(error) 24 | } 25 | 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/client/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Claudia CRM infrastrucuture as code - GraphQL Backend 4 | 5 | Parameters: 6 | APIName: 7 | Type: String 8 | Description: This is the API name 9 | MinLength: 3 10 | MaxLength: 20 11 | Default: claudiacrm 12 | AllowedPattern: "^[a-zA-Z][a-zA-Z0-9_]*$" 13 | ConstraintDescription: The name should have min. 3 and max. 20 letters 14 | APIStage: 15 | Type: String 16 | Description: This is the API stage 17 | MinLength: 3 18 | MaxLength: 10 19 | Default: dev 20 | AllowedPattern: "^[a-zA-Z][a-zA-Z0-9_]*$" 21 | AllowedValues: 22 | - prod 23 | - test 24 | - dev 25 | ConstraintDescription: Must be one of the following values - prod, test or dev 26 | graphQLBucketName: 27 | Type: String 28 | Description: This is the s3 url to the bucket with GraphQL Schema specs 29 | MinLength: 3 30 | MaxLength: 50 31 | Default: com.claudiacrm-dev-graphql-specs 32 | AllowedPattern: "^[a-z0-9_.-]*$" 33 | ConstraintDescription: You need to create the bucket first, add schema and mapping templates 34 | 35 | Globals: 36 | Function: 37 | Runtime: nodejs8.10 38 | Timeout: 5 39 | MemorySize: 128 40 | Environment: 41 | Variables: 42 | TABLE_NAME: !Ref claudiaCrmMainDB 43 | USER_POOL_ID: !Ref claudiaCrmUserPool 44 | 45 | Resources: 46 | 47 | claudiaCrmMainDB: 48 | Type: AWS::DynamoDB::Table 49 | Description: The main table for contacts, companies, deals and activities 50 | Properties: 51 | TableName: !Sub ${APIName}-${APIStage} 52 | AttributeDefinitions: 53 | - AttributeName: userId 54 | AttributeType: S 55 | - AttributeName: entityId 56 | AttributeType: S 57 | KeySchema: 58 | - AttributeName: userId 59 | KeyType: HASH 60 | - AttributeName: entityId 61 | KeyType: RANGE 62 | ProvisionedThroughput: 63 | ReadCapacityUnits: 1 64 | WriteCapacityUnits: 1 65 | 66 | saveContact: 67 | Type: AWS::Serverless::Function 68 | Properties: 69 | Handler: src/backend/contacts/save-contact.handler 70 | Description: Lambda function that handles the saving of a contact 71 | FunctionName: !Sub ${APIName}-${APIStage}-saveContact 72 | Policies: 73 | - Version: 2012-10-17 74 | Statement: 75 | - Effect: Allow 76 | Action: 77 | - dynamodb:putItem 78 | Resource: !GetAtt claudiaCrmMainDB.Arn 79 | getContacts: 80 | Type: AWS::Serverless::Function 81 | Properties: 82 | Handler: src/backend/contacts/get-contacts.handler 83 | Description: Lambda function that returns all contacts 84 | FunctionName: !Sub ${APIName}-${APIStage}-getContacts 85 | Policies: 86 | - Version: 2012-10-17 87 | Statement: 88 | - Effect: Allow 89 | Action: 90 | - dynamodb:Query 91 | Resource: !GetAtt claudiaCrmMainDB.Arn 92 | assignContactToEntity: 93 | Type: AWS::Serverless::Function 94 | Properties: 95 | Handler: src/backend/contacts/assign-contact-to-entity.handler 96 | Description: Lambda function that assigns contact to an entity 97 | FunctionName: !Sub ${APIName}-${APIStage}-assignContactToEntity 98 | Policies: 99 | - Version: 2012-10-17 100 | Statement: 101 | - Effect: Allow 102 | Action: 103 | - dynamodb:putItem 104 | - dynamodb:Query 105 | - dynamodb:updateItem 106 | Resource: !GetAtt claudiaCrmMainDB.Arn 107 | saveCompany: 108 | Type: AWS::Serverless::Function 109 | Properties: 110 | Handler: src/backend/companies/save-company.handler 111 | Description: Lambda function that handles the saving of a company 112 | FunctionName: !Sub ${APIName}-${APIStage}-saveCompany 113 | Policies: 114 | - Version: 2012-10-17 115 | Statement: 116 | - Effect: Allow 117 | Action: 118 | - dynamodb:putItem 119 | Resource: !GetAtt claudiaCrmMainDB.Arn 120 | getCompanies: 121 | Type: AWS::Serverless::Function 122 | Properties: 123 | Handler: src/backend/companies/get-companies.handler 124 | Description: Lambda function that returns all companies 125 | FunctionName: !Sub ${APIName}-${APIStage}-getCompanies 126 | Policies: 127 | - Version: 2012-10-17 128 | Statement: 129 | - Effect: Allow 130 | Action: 131 | - dynamodb:Query 132 | Resource: !GetAtt claudiaCrmMainDB.Arn 133 | assignCompanyToEntity: 134 | Type: AWS::Serverless::Function 135 | Properties: 136 | Handler: src/backend/companies/assign-company-to-entity.handler 137 | Description: Lambda function that assigns company to an entity 138 | FunctionName: !Sub ${APIName}-${APIStage}-assignCompanyToEntity 139 | Policies: 140 | - Version: 2012-10-17 141 | Statement: 142 | - Effect: Allow 143 | Action: 144 | - dynamodb:putItem 145 | - dynamodb:Query 146 | - dynamodb:updateItem 147 | Resource: !GetAtt claudiaCrmMainDB.Arn 148 | saveDeal: 149 | Type: AWS::Serverless::Function 150 | Properties: 151 | Handler: src/backend/deals/save-deal.handler 152 | Description: Lambda function that handles the saving of a deal 153 | FunctionName: !Sub ${APIName}-${APIStage}-saveDeal 154 | Policies: 155 | - Version: 2012-10-17 156 | Statement: 157 | - Effect: Allow 158 | Action: 159 | - dynamodb:putItem 160 | Resource: !GetAtt claudiaCrmMainDB.Arn 161 | getDeals: 162 | Type: AWS::Serverless::Function 163 | Properties: 164 | Handler: src/backend/deals/get-deals.handler 165 | Description: Lambda function that returns all deals 166 | FunctionName: !Sub ${APIName}-${APIStage}-getDeals 167 | Policies: 168 | - Version: 2012-10-17 169 | Statement: 170 | - Effect: Allow 171 | Action: 172 | - dynamodb:Query 173 | Resource: !GetAtt claudiaCrmMainDB.Arn 174 | assignDealToEntity: 175 | Type: AWS::Serverless::Function 176 | Properties: 177 | Handler: src/backend/deals/assign-deal-to-entity.handler 178 | Description: Lambda function that assigns deal to an entity 179 | FunctionName: !Sub ${APIName}-${APIStage}-assignDealToEntity 180 | Policies: 181 | - Version: 2012-10-17 182 | Statement: 183 | - Effect: Allow 184 | Action: 185 | - dynamodb:putItem 186 | - dynamodb:Query 187 | - dynamodb:updateItem 188 | Resource: !GetAtt claudiaCrmMainDB.Arn 189 | saveActivity: 190 | Type: AWS::Serverless::Function 191 | Properties: 192 | Handler: src/backend/activities/save-activity.handler 193 | Description: Lambda function that handles the saving of an activity 194 | FunctionName: !Sub ${APIName}-${APIStage}-saveActivity 195 | Policies: 196 | - Version: 2012-10-17 197 | Statement: 198 | - Effect: Allow 199 | Action: 200 | - dynamodb:putItem 201 | Resource: !GetAtt claudiaCrmMainDB.Arn 202 | getActivities: 203 | Type: AWS::Serverless::Function 204 | Properties: 205 | Handler: src/backend/activities/get-activities.handler 206 | Description: Lambda function that returns all activities 207 | FunctionName: !Sub ${APIName}-${APIStage}-getActivities 208 | Policies: 209 | - Version: 2012-10-17 210 | Statement: 211 | - Effect: Allow 212 | Action: 213 | - dynamodb:Query 214 | Resource: !GetAtt claudiaCrmMainDB.Arn 215 | assignActivityToEntity: 216 | Type: AWS::Serverless::Function 217 | Properties: 218 | Handler: src/backend/activities/assign-activity-to-entity.handler 219 | Description: Lambda function that assigns activity to an entity 220 | FunctionName: !Sub ${APIName}-${APIStage}-assignActivityToEntity 221 | Policies: 222 | - Version: 2012-10-17 223 | Statement: 224 | - Effect: Allow 225 | Action: 226 | - dynamodb:putItem 227 | - dynamodb:Query 228 | - dynamodb:updateItem 229 | Resource: !GetAtt claudiaCrmMainDB.Arn 230 | assignOwnerToEntity: 231 | Type: AWS::Serverless::Function 232 | Properties: 233 | Handler: src/backend/users/assign-owner-to-entity.handler 234 | Description: Lambda function that assigns owner to an entity 235 | FunctionName: !Sub ${APIName}-${APIStage}-assignOwnerToEntity 236 | Policies: 237 | - Version: 2012-10-17 238 | Statement: 239 | - Effect: Allow 240 | Action: 241 | - dynamodb:putItem 242 | - dynamodb:Query 243 | - dynamodb:updateItem 244 | Resource: !GetAtt claudiaCrmMainDB.Arn 245 | - Version: 2012-10-17 246 | Statement: 247 | - Effect: Allow 248 | Action: 249 | - cognito-idp:ListUsers 250 | Resource: !GetAtt claudiaCrmUserPool.Arn 251 | saveActivityFromEntity: 252 | Type: AWS::Serverless::Function 253 | Properties: 254 | Handler: src/backend/activities/save-activity-from-entity.handler 255 | Description: > 256 | Lambda function that saves an activity from within an entity e.g. contact or company. 257 | Especially you need it when your are inside a contact and want to save some activity. 258 | You don't need to switch a view to create an activity to come back to assign one. 259 | FunctionName: !Sub ${APIName}-${APIStage}-saveActivityFromEntity 260 | Policies: 261 | - Version: 2012-10-17 262 | Statement: 263 | - Effect: Allow 264 | Action: 265 | - dynamodb:putItem 266 | - dynamodb:Query 267 | - dynamodb:updateItem 268 | Resource: !GetAtt claudiaCrmMainDB.Arn 269 | saveDealFromEntity: 270 | Type: AWS::Serverless::Function 271 | Properties: 272 | Handler: src/backend/deals/save-deal-from-entity.handler 273 | Description: > 274 | Lambda function that saves an deal from within an entity e.g. contact or company. 275 | Especially you need it when your are inside a contact and want to save a new deal. 276 | You don't need to switch the view to create a deal to come back to assign one. 277 | FunctionName: !Sub ${APIName}-${APIStage}-saveDealFromEntity 278 | Policies: 279 | - Version: 2012-10-17 280 | Statement: 281 | - Effect: Allow 282 | Action: 283 | - dynamodb:putItem 284 | - dynamodb:Query 285 | - dynamodb:updateItem 286 | Resource: !GetAtt claudiaCrmMainDB.Arn 287 | getCognitoUserList: 288 | Type: AWS::Serverless::Function 289 | Properties: 290 | Handler: src/backend/users/get-cognito-user-list.handler 291 | Description: Lambda function that returns all cognito users from a specific company 292 | FunctionName: !Sub ${APIName}-${APIStage}-getCognitoUserList 293 | Policies: 294 | - Version: 2012-10-17 295 | Statement: 296 | - Effect: Allow 297 | Action: 298 | - cognito-idp:ListUsers 299 | Resource: !GetAtt claudiaCrmUserPool.Arn 300 | inviteUser: 301 | Type: AWS::Serverless::Function 302 | Properties: 303 | Handler: src/backend/users/invite-user.handler 304 | Description: Lambda function that invites new users and assigns them to the right groups 305 | FunctionName: !Sub ${APIName}-${APIStage}-inviteUser 306 | Policies: 307 | - Version: 2012-10-17 308 | Statement: 309 | - Effect: Allow 310 | Action: 311 | - cognito-idp:AdminCreateUser 312 | - cognito-idp:AdminAddUserToGroup 313 | Resource: !GetAtt claudiaCrmUserPool.Arn 314 | 315 | 316 | appSyncInvokeLambdaFunction: 317 | Type: AWS::IAM::ManagedPolicy 318 | Properties: 319 | Description: Managed policy to allow AppSync to invoke Lambda functions 320 | Path: /appsync/ 321 | PolicyDocument: 322 | Version: 2012-10-17 323 | Statement: 324 | - Effect: Allow 325 | Action: 326 | - lambda:InvokeFunction 327 | Resource: 328 | - !GetAtt saveContact.Arn 329 | - !GetAtt saveCompany.Arn 330 | - !GetAtt saveDeal.Arn 331 | - !GetAtt saveActivity.Arn 332 | - !GetAtt getCognitoUserList.Arn 333 | - !GetAtt saveDealFromEntity.Arn 334 | - !GetAtt saveActivityFromEntity.Arn 335 | - !GetAtt assignOwnerToEntity.Arn 336 | - !GetAtt assignActivityToEntity.Arn 337 | - !GetAtt getActivities.Arn 338 | - !GetAtt assignDealToEntity.Arn 339 | - !GetAtt getDeals.Arn 340 | - !GetAtt assignCompanyToEntity.Arn 341 | - !GetAtt getCompanies.Arn 342 | - !GetAtt assignContactToEntity.Arn 343 | - !GetAtt getContacts.Arn 344 | - !GetAtt inviteUser.Arn 345 | appSyncLambdaRole: 346 | Type: AWS::IAM::Role 347 | Properties: 348 | RoleName: !Sub ${APIName}-${APIStage}-appsync-invoke-lambda 349 | ManagedPolicyArns: 350 | - !Ref appSyncInvokeLambdaFunction 351 | AssumeRolePolicyDocument: 352 | Version: 2012-10-17 353 | Statement: 354 | - Effect: Allow 355 | Action: 356 | - sts:AssumeRole 357 | Principal: 358 | Service: 359 | - appsync.amazonaws.com 360 | DependsOn: 361 | - appSyncInvokeLambdaFunction 362 | 363 | claudiaCrmUserPool: 364 | Type: AWS::Cognito::UserPool 365 | Properties: 366 | UserPoolName: !Sub ${APIName}-${APIStage} 367 | UsernameAttributes: 368 | - email 369 | UserPoolTags: 370 | Name: Name 371 | Value: !Sub ${APIName}-${APIStage} 372 | Schema: 373 | - Name: email 374 | Required: true 375 | Mutable: true 376 | - Name: preferred_username 377 | Required: true 378 | Mutable: true 379 | - Name: name 380 | Required: false 381 | Mutable: true 382 | - Name: family_name 383 | Required: false 384 | Mutable: true 385 | 386 | claudiaCrmPoolClient: 387 | Type: AWS::Cognito::UserPoolClient 388 | Properties: 389 | UserPoolId: !Ref claudiaCrmUserPool 390 | 391 | claudiaCrmAppSyncApi: 392 | Type: AWS::AppSync::GraphQLApi 393 | Description: The GraphQL API for Claudia Crm 394 | Properties: 395 | AuthenticationType: AMAZON_COGNITO_USER_POOLS 396 | Name: !Sub ${APIName}-${APIStage} 397 | UserPoolConfig: 398 | UserPoolId: !Ref claudiaCrmUserPool 399 | AwsRegion: !Sub ${AWS::Region} 400 | DefaultAction: DENY 401 | 402 | claudiaCrmAppSyncSchema: 403 | Type: AWS::AppSync::GraphQLSchema 404 | Properties: 405 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 406 | DefinitionS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/schema.graphql'] ] 407 | 408 | 409 | saveContactDataSource: 410 | Type: AWS::AppSync::DataSource 411 | Properties: 412 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 413 | Name: !Sub ${APIName}SaveContact 414 | Description: Data source for saving a contact to a database 415 | Type: AWS_LAMBDA 416 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 417 | LambdaConfig: 418 | LambdaFunctionArn: !GetAtt saveContact.Arn 419 | saveCompanyDataSource: 420 | Type: AWS::AppSync::DataSource 421 | Properties: 422 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 423 | Name: !Sub ${APIName}SaveCompany 424 | Description: Data source for saving a contact to a database 425 | Type: AWS_LAMBDA 426 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 427 | LambdaConfig: 428 | LambdaFunctionArn: !GetAtt saveCompany.Arn 429 | saveDealDataSource: 430 | Type: AWS::AppSync::DataSource 431 | Properties: 432 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 433 | Name: !Sub ${APIName}SaveDeal 434 | Description: Data source for saving a contact to a database 435 | Type: AWS_LAMBDA 436 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 437 | LambdaConfig: 438 | LambdaFunctionArn: !GetAtt saveDeal.Arn 439 | saveActivityDataSource: 440 | Type: AWS::AppSync::DataSource 441 | Properties: 442 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 443 | Name: !Sub ${APIName}SaveActivity 444 | Description: Data source for saving a contact to a database 445 | Type: AWS_LAMBDA 446 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 447 | LambdaConfig: 448 | LambdaFunctionArn: !GetAtt saveActivity.Arn 449 | assignContactToEntityDataSource: 450 | Type: AWS::AppSync::DataSource 451 | Properties: 452 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 453 | Name: !Sub ${APIName}assignContactToEntity 454 | Description: Data source for assigning a contact from an entity view e.g. from contact, deal. 455 | Type: AWS_LAMBDA 456 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 457 | LambdaConfig: 458 | LambdaFunctionArn: !GetAtt assignContactToEntity.Arn 459 | assignCompanyToEntityDataSource: 460 | Type: AWS::AppSync::DataSource 461 | Properties: 462 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 463 | Name: !Sub ${APIName}assignCompanyToEntity 464 | Description: Data source for assigning a company from an entity view e.g. from contact, deal. 465 | Type: AWS_LAMBDA 466 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 467 | LambdaConfig: 468 | LambdaFunctionArn: !GetAtt assignCompanyToEntity.Arn 469 | assignDealToEntityDataSource: 470 | Type: AWS::AppSync::DataSource 471 | Properties: 472 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 473 | Name: !Sub ${APIName}assignDealToEntity 474 | Description: Data source for assigning a deal from an entity view e.g. from contact, deal. 475 | Type: AWS_LAMBDA 476 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 477 | LambdaConfig: 478 | LambdaFunctionArn: !GetAtt assignDealToEntity.Arn 479 | assignActivityToEntityDataSource: 480 | Type: AWS::AppSync::DataSource 481 | Properties: 482 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 483 | Name: !Sub ${APIName}assignActivityToEntity 484 | Description: Data source for assigning an activity from an entity view e.g. from contact, deal. 485 | Type: AWS_LAMBDA 486 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 487 | LambdaConfig: 488 | LambdaFunctionArn: !GetAtt assignActivityToEntity.Arn 489 | assignOwnerToEntityDataSource: 490 | Type: AWS::AppSync::DataSource 491 | Properties: 492 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 493 | Name: !Sub ${APIName}assignOwnerToEntity 494 | Description: Data source for assigning owner from an entity view e.g. from contact, deal. 495 | Type: AWS_LAMBDA 496 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 497 | LambdaConfig: 498 | LambdaFunctionArn: !GetAtt assignOwnerToEntity.Arn 499 | saveActivityFromEntityDataSource: 500 | Type: AWS::AppSync::DataSource 501 | Properties: 502 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 503 | Name: !Sub ${APIName}saveActivityFromEntity 504 | Description: Data source for saving an activity from an entity view e.g. from contact, deal. 505 | Type: AWS_LAMBDA 506 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 507 | LambdaConfig: 508 | LambdaFunctionArn: !GetAtt saveActivityFromEntity.Arn 509 | saveDealFromEntityDataSource: 510 | Type: AWS::AppSync::DataSource 511 | Properties: 512 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 513 | Name: !Sub ${APIName}saveDealFromEntity 514 | Description: Data source for saving a deal from an entity view e.g. from contact, deal. 515 | Type: AWS_LAMBDA 516 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 517 | LambdaConfig: 518 | LambdaFunctionArn: !GetAtt saveDealFromEntity.Arn 519 | getActivitiesDataSource: 520 | Type: AWS::AppSync::DataSource 521 | Properties: 522 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 523 | Name: !Sub ${APIName}getActivities 524 | Description: Data source for retrieving activities 525 | Type: AWS_LAMBDA 526 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 527 | LambdaConfig: 528 | LambdaFunctionArn: !GetAtt getActivities.Arn 529 | getCognitoUserListDataSource: 530 | Type: AWS::AppSync::DataSource 531 | Properties: 532 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 533 | Name: !Sub ${APIName}getCognitoUserList 534 | Description: Data source for retrieving cognito users from the same company 535 | Type: AWS_LAMBDA 536 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 537 | LambdaConfig: 538 | LambdaFunctionArn: !GetAtt getCognitoUserList.Arn 539 | getCompaniesDataSource: 540 | Type: AWS::AppSync::DataSource 541 | Properties: 542 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 543 | Name: !Sub ${APIName}getCompanies 544 | Description: Data source for retrieving companies 545 | Type: AWS_LAMBDA 546 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 547 | LambdaConfig: 548 | LambdaFunctionArn: !GetAtt getCompanies.Arn 549 | getContactsDataSource: 550 | Type: AWS::AppSync::DataSource 551 | Properties: 552 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 553 | Name: !Sub ${APIName}getContacts 554 | Description: Data source for retrieving contacts 555 | Type: AWS_LAMBDA 556 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 557 | LambdaConfig: 558 | LambdaFunctionArn: !GetAtt getContacts.Arn 559 | getDealsDataSource: 560 | Type: AWS::AppSync::DataSource 561 | Properties: 562 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 563 | Name: !Sub ${APIName}getDeals 564 | Description: Data source for retrieving deals 565 | Type: AWS_LAMBDA 566 | ServiceRoleArn: !GetAtt appSyncLambdaRole.Arn 567 | LambdaConfig: 568 | LambdaFunctionArn: !GetAtt getDeals.Arn 569 | 570 | 571 | saveContactResolver: 572 | Type: AWS::AppSync::Resolver 573 | DependsOn: claudiaCrmAppSyncSchema 574 | Properties: 575 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 576 | TypeName: Mutation 577 | DataSourceName: !GetAtt saveContactDataSource.Name 578 | FieldName: saveContact 579 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 580 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 581 | saveCompanyResolver: 582 | Type: AWS::AppSync::Resolver 583 | DependsOn: claudiaCrmAppSyncSchema 584 | Properties: 585 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 586 | TypeName: Mutation 587 | DataSourceName: !GetAtt saveCompanyDataSource.Name 588 | FieldName: saveCompany 589 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 590 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 591 | saveDealResolver: 592 | Type: AWS::AppSync::Resolver 593 | DependsOn: claudiaCrmAppSyncSchema 594 | Properties: 595 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 596 | TypeName: Mutation 597 | DataSourceName: !GetAtt saveDealDataSource.Name 598 | FieldName: saveDeal 599 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 600 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 601 | saveActivityResolver: 602 | Type: AWS::AppSync::Resolver 603 | DependsOn: claudiaCrmAppSyncSchema 604 | Properties: 605 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 606 | TypeName: Mutation 607 | DataSourceName: !GetAtt saveActivityDataSource.Name 608 | FieldName: saveActivity 609 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 610 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 611 | assignContactToEntityResolver: 612 | Type: AWS::AppSync::Resolver 613 | DependsOn: claudiaCrmAppSyncSchema 614 | Properties: 615 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 616 | TypeName: Mutation 617 | DataSourceName: !GetAtt assignContactToEntityDataSource.Name 618 | FieldName: assignContactToEntity 619 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 620 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 621 | assignCompanyToEntityResolver: 622 | Type: AWS::AppSync::Resolver 623 | DependsOn: claudiaCrmAppSyncSchema 624 | Properties: 625 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 626 | TypeName: Mutation 627 | DataSourceName: !GetAtt assignCompanyToEntityDataSource.Name 628 | FieldName: assignCompanyToEntity 629 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 630 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 631 | assignDealToEntityResolver: 632 | Type: AWS::AppSync::Resolver 633 | DependsOn: claudiaCrmAppSyncSchema 634 | Properties: 635 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 636 | TypeName: Mutation 637 | DataSourceName: !GetAtt assignDealToEntityDataSource.Name 638 | FieldName: assignDealToEntity 639 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 640 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 641 | assignActivityToEntityResolver: 642 | Type: AWS::AppSync::Resolver 643 | DependsOn: claudiaCrmAppSyncSchema 644 | Properties: 645 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 646 | TypeName: Mutation 647 | DataSourceName: !GetAtt assignActivityToEntityDataSource.Name 648 | FieldName: assignActivityToEntity 649 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 650 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 651 | assignOwnerToEntityResolver: 652 | Type: AWS::AppSync::Resolver 653 | DependsOn: claudiaCrmAppSyncSchema 654 | Properties: 655 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 656 | TypeName: Mutation 657 | DataSourceName: !GetAtt assignOwnerToEntityDataSource.Name 658 | FieldName: assignOwnerToEntity 659 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 660 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 661 | saveActivityFromEntityResolver: 662 | Type: AWS::AppSync::Resolver 663 | DependsOn: claudiaCrmAppSyncSchema 664 | Properties: 665 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 666 | TypeName: Mutation 667 | DataSourceName: !GetAtt saveActivityFromEntityDataSource.Name 668 | FieldName: saveActivityFromEntity 669 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 670 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 671 | saveDealFromEntityResolver: 672 | Type: AWS::AppSync::Resolver 673 | DependsOn: claudiaCrmAppSyncSchema 674 | Properties: 675 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 676 | TypeName: Mutation 677 | DataSourceName: !GetAtt saveDealFromEntityDataSource.Name 678 | FieldName: saveDealFromEntity 679 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 680 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 681 | getActivitiesResolver: 682 | Type: AWS::AppSync::Resolver 683 | DependsOn: claudiaCrmAppSyncSchema 684 | Properties: 685 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 686 | TypeName: Query 687 | DataSourceName: !GetAtt getActivitiesDataSource.Name 688 | FieldName: getActivities 689 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 690 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 691 | getCognitoUserListResolver: 692 | Type: AWS::AppSync::Resolver 693 | DependsOn: claudiaCrmAppSyncSchema 694 | Properties: 695 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 696 | TypeName: Query 697 | DataSourceName: !GetAtt getCognitoUserListDataSource.Name 698 | FieldName: getCognitoUserList 699 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 700 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 701 | getCompaniesResolver: 702 | Type: AWS::AppSync::Resolver 703 | DependsOn: claudiaCrmAppSyncSchema 704 | Properties: 705 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 706 | TypeName: Query 707 | DataSourceName: !GetAtt getCompaniesDataSource.Name 708 | FieldName: getCompanies 709 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 710 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 711 | getContactsResolver: 712 | Type: AWS::AppSync::Resolver 713 | DependsOn: claudiaCrmAppSyncSchema 714 | Properties: 715 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 716 | TypeName: Query 717 | DataSourceName: !GetAtt getContactsDataSource.Name 718 | FieldName: getContacts 719 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 720 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 721 | getDealsResolver: 722 | Type: AWS::AppSync::Resolver 723 | DependsOn: claudiaCrmAppSyncSchema 724 | Properties: 725 | ApiId: !GetAtt claudiaCrmAppSyncApi.ApiId 726 | TypeName: Query 727 | DataSourceName: !GetAtt getDealsDataSource.Name 728 | FieldName: getDeals 729 | RequestMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/request-mapping-template.txt'] ] 730 | ResponseMappingTemplateS3Location: !Join ['',['s3://', !Ref graphQLBucketName, '/response-mapping-template.txt'] ] 731 | 732 | 733 | Outputs: 734 | AppSyncGraphQLApiEndpoint: 735 | Description: Here is your GraphQL endpoint URL 736 | Value: !GetAtt claudiaCrmAppSyncApi.GraphQLUrl 737 | AppSyncGraphQLArn: 738 | Description: Here is your GraphQL Arn 739 | Value: !GetAtt claudiaCrmAppSyncApi.Arn 740 | --------------------------------------------------------------------------------