diff --git a/.circleci/config.base.yml b/.circleci/config.base.yml index 1bf9452036a..6e96920be40 100644 --- a/.circleci/config.base.yml +++ b/.circleci/config.base.yml @@ -885,26 +885,11 @@ workflows: - npm-publish requires: - test - - mock_e2e_tests - - graphql_e2e_tests - - integration_test - - done_with_pkg_linux_e2e_tests - - amplify_sudo_install_test - - amplify_console_integration_tests - - amplify_migration_tests_latest - - amplify_migration_tests_v4 - - amplify_migration_tests_v4_30_0 - - amplify_migration_tests_non_multi_env_layers - - amplify_migration_tests_multi_env_layers - github_prerelease_install_sanity_check filters: branches: only: - - release - - master - - beta - - /tagged-release\/.*/ - - /tagged-release-without-e2e-tests\/.*/ + - tagged-release-without-e2e-tests/graphql-vnext-dev-preview - github_release: context: github-publish requires: diff --git a/.circleci/config.yml b/.circleci/config.yml index ce6732cbf98..2dab06d7424 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1195,6 +1195,14 @@ jobs: environment: TEST_SUITE: src/__tests__/schema-iterative-update-locking.test.ts CLI_REGION: eu-central-1 + sandbox-mode-amplify_e2e_tests: + working_directory: ~/repo + docker: *ref_1 + resource_class: large + steps: *ref_5 + environment: + TEST_SUITE: src/__tests__/sandbox-mode.test.ts + CLI_REGION: ap-northeast-1 s3-sse-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1202,7 +1210,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/s3-sse.test.ts - CLI_REGION: ap-northeast-1 + CLI_REGION: ap-southeast-1 pull-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1210,7 +1218,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/pull.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 migration-node-function-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1218,7 +1226,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/migration/node.function.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: us-east-2 layer-2-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1226,7 +1234,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/layer-2.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-2 iam-permissions-boundary-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1234,7 +1242,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/iam-permissions-boundary.test.ts - CLI_REGION: us-west-2 + CLI_REGION: eu-west-2 hooks-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1242,7 +1250,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/hooks.test.ts - CLI_REGION: eu-west-2 + CLI_REGION: eu-central-1 function_7-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1250,7 +1258,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/function_7.test.ts - CLI_REGION: eu-central-1 + CLI_REGION: ap-northeast-1 function_6-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1258,7 +1266,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/function_6.test.ts - CLI_REGION: ap-northeast-1 + CLI_REGION: ap-southeast-1 function_5-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1266,7 +1274,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/function_5.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 frontend_config_drift-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1274,7 +1282,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/frontend_config_drift.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: us-east-2 container-hosting-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1282,7 +1290,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/container-hosting.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-2 configure-project-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1290,7 +1298,7 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/configure-project.test.ts - CLI_REGION: us-west-2 + CLI_REGION: eu-west-2 auth_6-amplify_e2e_tests: working_directory: ~/repo docker: *ref_1 @@ -1298,14 +1306,6 @@ jobs: steps: *ref_5 environment: TEST_SUITE: src/__tests__/auth_6.test.ts - CLI_REGION: eu-west-2 - api_5-amplify_e2e_tests: - working_directory: ~/repo - docker: *ref_1 - resource_class: large - steps: *ref_5 - environment: - TEST_SUITE: src/__tests__/api_5.test.ts CLI_REGION: eu-central-1 api_4-amplify_e2e_tests: working_directory: ~/repo @@ -1985,6 +1985,16 @@ jobs: TEST_SUITE: src/__tests__/schema-iterative-update-locking.test.ts CLI_REGION: eu-central-1 steps: *ref_6 + sandbox-mode-amplify_e2e_tests_pkg_linux: + working_directory: ~/repo + docker: *ref_1 + resource_class: large + environment: + AMPLIFY_DIR: /home/circleci/repo/out + AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux + TEST_SUITE: src/__tests__/sandbox-mode.test.ts + CLI_REGION: ap-northeast-1 + steps: *ref_6 s3-sse-amplify_e2e_tests_pkg_linux: working_directory: ~/repo docker: *ref_1 @@ -1993,7 +2003,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/s3-sse.test.ts - CLI_REGION: ap-northeast-1 + CLI_REGION: ap-southeast-1 steps: *ref_6 pull-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2003,7 +2013,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/pull.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 steps: *ref_6 migration-node-function-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2013,7 +2023,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/migration/node.function.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: us-east-2 steps: *ref_6 layer-2-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2023,7 +2033,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/layer-2.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-2 steps: *ref_6 iam-permissions-boundary-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2033,7 +2043,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/iam-permissions-boundary.test.ts - CLI_REGION: us-west-2 + CLI_REGION: eu-west-2 steps: *ref_6 hooks-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2043,7 +2053,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/hooks.test.ts - CLI_REGION: eu-west-2 + CLI_REGION: eu-central-1 steps: *ref_6 function_7-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2053,7 +2063,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/function_7.test.ts - CLI_REGION: eu-central-1 + CLI_REGION: ap-northeast-1 steps: *ref_6 function_6-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2063,7 +2073,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/function_6.test.ts - CLI_REGION: ap-northeast-1 + CLI_REGION: ap-southeast-1 steps: *ref_6 function_5-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2073,7 +2083,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/function_5.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 steps: *ref_6 frontend_config_drift-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2083,7 +2093,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/frontend_config_drift.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: us-east-2 steps: *ref_6 container-hosting-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2093,7 +2103,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/container-hosting.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-2 steps: *ref_6 configure-project-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2103,7 +2113,7 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/configure-project.test.ts - CLI_REGION: us-west-2 + CLI_REGION: eu-west-2 steps: *ref_6 auth_6-amplify_e2e_tests_pkg_linux: working_directory: ~/repo @@ -2113,16 +2123,6 @@ jobs: AMPLIFY_DIR: /home/circleci/repo/out AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux TEST_SUITE: src/__tests__/auth_6.test.ts - CLI_REGION: eu-west-2 - steps: *ref_6 - api_5-amplify_e2e_tests_pkg_linux: - working_directory: ~/repo - docker: *ref_1 - resource_class: large - environment: - AMPLIFY_DIR: /home/circleci/repo/out - AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux - TEST_SUITE: src/__tests__/api_5.test.ts CLI_REGION: eu-central-1 steps: *ref_6 api_4-amplify_e2e_tests_pkg_linux: @@ -2239,62 +2239,62 @@ workflows: requires: - interactions-amplify_e2e_tests - datastore-modelgen-amplify_e2e_tests - - layer-2-amplify_e2e_tests - - container-hosting-amplify_e2e_tests + - migration-node-function-amplify_e2e_tests + - frontend_config_drift-amplify_e2e_tests - schema-data-access-patterns-amplify_e2e_tests - init-special-case-amplify_e2e_tests - - iam-permissions-boundary-amplify_e2e_tests - - configure-project-amplify_e2e_tests + - layer-2-amplify_e2e_tests + - container-hosting-amplify_e2e_tests - schema-versioned-amplify_e2e_tests - plugin-amplify_e2e_tests - - hooks-amplify_e2e_tests - - auth_6-amplify_e2e_tests + - iam-permissions-boundary-amplify_e2e_tests + - configure-project-amplify_e2e_tests - notifications-amplify_e2e_tests - schema-iterative-update-locking-amplify_e2e_tests - - function_7-amplify_e2e_tests - - api_5-amplify_e2e_tests + - hooks-amplify_e2e_tests + - auth_6-amplify_e2e_tests - tags-amplify_e2e_tests - - s3-sse-amplify_e2e_tests - - function_6-amplify_e2e_tests + - sandbox-mode-amplify_e2e_tests + - function_7-amplify_e2e_tests - api_4-amplify_e2e_tests - amplify-app-amplify_e2e_tests - init-amplify_e2e_tests - - pull-amplify_e2e_tests - - function_5-amplify_e2e_tests + - s3-sse-amplify_e2e_tests + - function_6-amplify_e2e_tests - schema-predictions-amplify_e2e_tests - amplify-configure-amplify_e2e_tests - - migration-node-function-amplify_e2e_tests - - frontend_config_drift-amplify_e2e_tests + - pull-amplify_e2e_tests + - function_5-amplify_e2e_tests - done_with_pkg_linux_e2e_tests: requires: - interactions-amplify_e2e_tests_pkg_linux - datastore-modelgen-amplify_e2e_tests_pkg_linux - - layer-2-amplify_e2e_tests_pkg_linux - - container-hosting-amplify_e2e_tests_pkg_linux + - migration-node-function-amplify_e2e_tests_pkg_linux + - frontend_config_drift-amplify_e2e_tests_pkg_linux - schema-data-access-patterns-amplify_e2e_tests_pkg_linux - init-special-case-amplify_e2e_tests_pkg_linux - - iam-permissions-boundary-amplify_e2e_tests_pkg_linux - - configure-project-amplify_e2e_tests_pkg_linux + - layer-2-amplify_e2e_tests_pkg_linux + - container-hosting-amplify_e2e_tests_pkg_linux - schema-versioned-amplify_e2e_tests_pkg_linux - plugin-amplify_e2e_tests_pkg_linux - - hooks-amplify_e2e_tests_pkg_linux - - auth_6-amplify_e2e_tests_pkg_linux + - iam-permissions-boundary-amplify_e2e_tests_pkg_linux + - configure-project-amplify_e2e_tests_pkg_linux - notifications-amplify_e2e_tests_pkg_linux - schema-iterative-update-locking-amplify_e2e_tests_pkg_linux - - function_7-amplify_e2e_tests_pkg_linux - - api_5-amplify_e2e_tests_pkg_linux + - hooks-amplify_e2e_tests_pkg_linux + - auth_6-amplify_e2e_tests_pkg_linux - tags-amplify_e2e_tests_pkg_linux - - s3-sse-amplify_e2e_tests_pkg_linux - - function_6-amplify_e2e_tests_pkg_linux + - sandbox-mode-amplify_e2e_tests_pkg_linux + - function_7-amplify_e2e_tests_pkg_linux - api_4-amplify_e2e_tests_pkg_linux - amplify-app-amplify_e2e_tests_pkg_linux - init-amplify_e2e_tests_pkg_linux - - pull-amplify_e2e_tests_pkg_linux - - function_5-amplify_e2e_tests_pkg_linux + - s3-sse-amplify_e2e_tests_pkg_linux + - function_6-amplify_e2e_tests_pkg_linux - schema-predictions-amplify_e2e_tests_pkg_linux - amplify-configure-amplify_e2e_tests_pkg_linux - - migration-node-function-amplify_e2e_tests_pkg_linux - - frontend_config_drift-amplify_e2e_tests_pkg_linux + - pull-amplify_e2e_tests_pkg_linux + - function_5-amplify_e2e_tests_pkg_linux - amplify_migration_tests_latest: context: - amplify-ecr-image-pull @@ -2412,26 +2412,11 @@ workflows: - npm-publish requires: - test - - mock_e2e_tests - - graphql_e2e_tests - - integration_test - - done_with_pkg_linux_e2e_tests - - amplify_sudo_install_test - - amplify_console_integration_tests - - amplify_migration_tests_latest - - amplify_migration_tests_v4 - - amplify_migration_tests_v4_30_0 - - amplify_migration_tests_non_multi_env_layers - - amplify_migration_tests_multi_env_layers - github_prerelease_install_sanity_check filters: branches: only: - - release - - master - - beta - - /tagged-release\/.*/ - - /tagged-release-without-e2e-tests\/.*/ + - tagged-release-without-e2e-tests/graphql-vnext-dev-preview - github_release: context: github-publish requires: @@ -2510,13 +2495,13 @@ workflows: filters: *ref_10 requires: - function_2-amplify_e2e_tests - - layer-2-amplify_e2e_tests: + - migration-node-function-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - schema-key-amplify_e2e_tests - - container-hosting-amplify_e2e_tests: + - frontend_config_drift-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -2582,13 +2567,13 @@ workflows: filters: *ref_10 requires: - delete-amplify_e2e_tests - - iam-permissions-boundary-amplify_e2e_tests: + - layer-2-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - schema-auth-10-amplify_e2e_tests - - configure-project-amplify_e2e_tests: + - container-hosting-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -2648,13 +2633,13 @@ workflows: filters: *ref_10 requires: - schema-auth-7-amplify_e2e_tests - - hooks-amplify_e2e_tests: + - iam-permissions-boundary-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - schema-auth-3-amplify_e2e_tests - - auth_6-amplify_e2e_tests: + - configure-project-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -2720,13 +2705,13 @@ workflows: filters: *ref_10 requires: - schema-iterative-update-1-amplify_e2e_tests - - function_7-amplify_e2e_tests: + - hooks-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - geo-remove-amplify_e2e_tests - - api_5-amplify_e2e_tests: + - auth_6-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -2786,13 +2771,13 @@ workflows: filters: *ref_10 requires: - migration-api-key-migration1-amplify_e2e_tests - - s3-sse-amplify_e2e_tests: + - sandbox-mode-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - function_3-amplify_e2e_tests - - function_6-amplify_e2e_tests: + - function_7-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -2864,13 +2849,13 @@ workflows: filters: *ref_10 requires: - auth_5-amplify_e2e_tests - - pull-amplify_e2e_tests: + - s3-sse-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - geo-add-amplify_e2e_tests - - function_5-amplify_e2e_tests: + - function_6-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -2930,13 +2915,13 @@ workflows: filters: *ref_10 requires: - auth_3-amplify_e2e_tests - - migration-node-function-amplify_e2e_tests: + - pull-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 requires: - auth_1-amplify_e2e_tests - - frontend_config_drift-amplify_e2e_tests: + - function_5-amplify_e2e_tests: context: *ref_8 post-steps: *ref_9 filters: *ref_10 @@ -3016,13 +3001,13 @@ workflows: filters: *ref_13 requires: - function_2-amplify_e2e_tests_pkg_linux - - layer-2-amplify_e2e_tests_pkg_linux: + - migration-node-function-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - schema-key-amplify_e2e_tests_pkg_linux - - container-hosting-amplify_e2e_tests_pkg_linux: + - frontend_config_drift-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 @@ -3092,13 +3077,13 @@ workflows: filters: *ref_13 requires: - delete-amplify_e2e_tests_pkg_linux - - iam-permissions-boundary-amplify_e2e_tests_pkg_linux: + - layer-2-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - schema-auth-10-amplify_e2e_tests_pkg_linux - - configure-project-amplify_e2e_tests_pkg_linux: + - container-hosting-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 @@ -3162,13 +3147,13 @@ workflows: filters: *ref_13 requires: - schema-auth-7-amplify_e2e_tests_pkg_linux - - hooks-amplify_e2e_tests_pkg_linux: + - iam-permissions-boundary-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - schema-auth-3-amplify_e2e_tests_pkg_linux - - auth_6-amplify_e2e_tests_pkg_linux: + - configure-project-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 @@ -3238,13 +3223,13 @@ workflows: filters: *ref_13 requires: - schema-iterative-update-1-amplify_e2e_tests_pkg_linux - - function_7-amplify_e2e_tests_pkg_linux: + - hooks-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - geo-remove-amplify_e2e_tests_pkg_linux - - api_5-amplify_e2e_tests_pkg_linux: + - auth_6-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 @@ -3308,13 +3293,13 @@ workflows: filters: *ref_13 requires: - migration-api-key-migration1-amplify_e2e_tests_pkg_linux - - s3-sse-amplify_e2e_tests_pkg_linux: + - sandbox-mode-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - function_3-amplify_e2e_tests_pkg_linux - - function_6-amplify_e2e_tests_pkg_linux: + - function_7-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 @@ -3390,13 +3375,13 @@ workflows: filters: *ref_13 requires: - auth_5-amplify_e2e_tests_pkg_linux - - pull-amplify_e2e_tests_pkg_linux: + - s3-sse-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - geo-add-amplify_e2e_tests_pkg_linux - - function_5-amplify_e2e_tests_pkg_linux: + - function_6-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 @@ -3460,13 +3445,13 @@ workflows: filters: *ref_13 requires: - auth_3-amplify_e2e_tests_pkg_linux - - migration-node-function-amplify_e2e_tests_pkg_linux: + - pull-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 requires: - auth_1-amplify_e2e_tests_pkg_linux - - frontend_config_drift-amplify_e2e_tests_pkg_linux: + - function_5-amplify_e2e_tests_pkg_linux: context: *ref_11 post-steps: *ref_12 filters: *ref_13 diff --git a/package.json b/package.json index e8b1a70f954..96a99612376 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "publish:beta": "lerna publish --exact --dist-tag=beta --preid=beta --conventional-commits --conventional-prerelease --message 'chore(release): Publish [ci skip]' --no-verify-access --yes", "publish:tag": "lerna publish --exact --dist-tag=$NPM_TAG --preid=$NPM_TAG --conventional-commits --conventional-prerelease --message 'chore(release): Publish tagged release $NPM_TAG [ci skip]' --no-verify-access --yes", "publish:release": "lerna publish --conventional-commits --exact --yes --message 'chore(release): Publish [ci skip]' --no-verify-access", + "publish:tagged-release-without-e2e-tests/graphql-vnext-dev-preview": "lerna publish --exact --dist-tag=graphql-vnext-dev-preview --preid=graphql-vnext-dev-preview --conventional-commits --conventional-prerelease --message 'chore(release): Publish [ci skip]' --no-verify-access --yes", "postpublish:release": "git fetch . release:master && git push origin master", "yarn-use-bash": "yarn config set script-shell /bin/bash", "verdaccio-start": "source .circleci/local_publish_helpers.sh && startLocalRegistry \"$(pwd)/.circleci/verdaccio.yaml\"", diff --git a/packages/amplify-app/CHANGELOG.md b/packages/amplify-app/CHANGELOG.md index 176be919d08..14e901c9af4 100644 --- a/packages/amplify-app/CHANGELOG.md +++ b/packages/amplify-app/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.13-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-app@3.0.12...amplify-app@3.0.13-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-app + + + + + ## [3.0.12](https://github.com/aws-amplify/amplify-cli/compare/amplify-app@3.0.11...amplify-app@3.0.12) (2021-09-18) **Note:** Version bump only for package amplify-app diff --git a/packages/amplify-app/package.json b/packages/amplify-app/package.json index 905d7036292..0548c26dada 100644 --- a/packages/amplify-app/package.json +++ b/packages/amplify-app/package.json @@ -1,6 +1,6 @@ { "name": "amplify-app", - "version": "3.0.12", + "version": "3.0.13-graphql-vnext-dev-preview.0", "description": "Amplify CLI", "repository": { "type": "git", @@ -30,8 +30,8 @@ "dependencies": { "amplify-frontend-android": "2.15.4", "amplify-frontend-flutter": "0.4.4", - "amplify-frontend-ios": "2.20.14", - "amplify-frontend-javascript": "2.24.1", + "amplify-frontend-ios": "2.20.15-graphql-vnext-dev-preview.0", + "amplify-frontend-javascript": "2.24.2-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "execa": "^5.1.1", "fs-extra": "^8.1.0", diff --git a/packages/amplify-appsync-simulator/src/schema/directives/auth.ts b/packages/amplify-appsync-simulator/src/schema/directives/auth.ts index 342e0748ac8..4155df37f84 100644 --- a/packages/amplify-appsync-simulator/src/schema/directives/auth.ts +++ b/packages/amplify-appsync-simulator/src/schema/directives/auth.ts @@ -9,6 +9,7 @@ const AUTH_DIRECTIVES = { aws_api_key: 'directive @aws_api_key on FIELD_DEFINITION | OBJECT', aws_iam: 'directive @aws_iam on FIELD_DEFINITION | OBJECT', aws_oidc: 'directive @aws_oidc on FIELD_DEFINITION | OBJECT', + aws_lambda: 'directive @aws_lambda on FIELD_DEFINITION | OBJECT', aws_cognito_user_pools: 'directive @aws_cognito_user_pools(cognito_groups: [String!]) on FIELD_DEFINITION | OBJECT', aws_auth: 'directive @aws_auth(cognito_groups: [String!]!) on FIELD_DEFINITION', }; @@ -21,6 +22,7 @@ const AUTH_TYPE_TO_DIRECTIVE_MAP: { aws_auth: AmplifyAppSyncSimulatorAuthenticationType.AMAZON_COGNITO_USER_POOLS, aws_cognito_user_pools: AmplifyAppSyncSimulatorAuthenticationType.AMAZON_COGNITO_USER_POOLS, aws_oidc: AmplifyAppSyncSimulatorAuthenticationType.OPENID_CONNECT, + aws_lambda: AmplifyAppSyncSimulatorAuthenticationType.AWS_LAMBDA, }; export class AwsAuth extends AppSyncSimulatorDirectiveBase { diff --git a/packages/amplify-appsync-simulator/src/type-definition.ts b/packages/amplify-appsync-simulator/src/type-definition.ts index 93c15648a7b..a73591c3efb 100644 --- a/packages/amplify-appsync-simulator/src/type-definition.ts +++ b/packages/amplify-appsync-simulator/src/type-definition.ts @@ -78,6 +78,7 @@ export enum AmplifyAppSyncSimulatorAuthenticationType { AWS_IAM = 'AWS_IAM', AMAZON_COGNITO_USER_POOLS = 'AMAZON_COGNITO_USER_POOLS', OPENID_CONNECT = 'OPENID_CONNECT', + AWS_LAMBDA = 'AWS_LAMBDA' } export type AmplifyAppSyncAuthenticationProviderAPIConfig = { @@ -103,11 +104,20 @@ export type AmplifyAppSyncAuthenticationProviderOIDCConfig = { }; }; +export type AmplifyAppSyncAuthenticationProviderLambdaConfig = { + authenticationType: AmplifyAppSyncSimulatorAuthenticationType.AWS_LAMBDA; + lambdaAuthorizerConfig: { + AuthorizerUri: string; + AuthorizerResultTtlInSeconds?: number; + }; +}; + export type AmplifyAppSyncAuthenticationProviderConfig = | AmplifyAppSyncAuthenticationProviderAPIConfig | AmplifyAppSyncAuthenticationProviderIAMConfig | AmplifyAppSyncAuthenticationProviderCognitoConfig - | AmplifyAppSyncAuthenticationProviderOIDCConfig; + | AmplifyAppSyncAuthenticationProviderOIDCConfig + | AmplifyAppSyncAuthenticationProviderLambdaConfig; export type AmplifyAppSyncAPIConfig = { name: string; diff --git a/packages/amplify-category-analytics/CHANGELOG.md b/packages/amplify-category-analytics/CHANGELOG.md index f528e9ed360..93e77e59401 100644 --- a/packages/amplify-category-analytics/CHANGELOG.md +++ b/packages/amplify-category-analytics/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.21.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-analytics@2.21.21...amplify-category-analytics@2.21.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-category-analytics + + + + + ## [2.21.21](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-analytics@2.21.20...amplify-category-analytics@2.21.21) (2021-09-18) **Note:** Version bump only for package amplify-category-analytics diff --git a/packages/amplify-category-analytics/package.json b/packages/amplify-category-analytics/package.json index e51528a9a8b..5372b1ff13c 100644 --- a/packages/amplify-category-analytics/package.json +++ b/packages/amplify-category-analytics/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-analytics", - "version": "2.21.21", + "version": "2.21.22-graphql-vnext-dev-preview.0", "description": "amplify-cli analytics plugin", "repository": { "type": "git", @@ -15,7 +15,7 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "fs-extra": "^8.1.0", "inquirer": "^7.3.3", "uuid": "^3.4.0" diff --git a/packages/amplify-category-api/CHANGELOG.md b/packages/amplify-category-api/CHANGELOG.md index 25ae29ee68d..0159aa98e43 100644 --- a/packages/amplify-category-api/CHANGELOG.md +++ b/packages/amplify-category-api/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.32.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-api@2.31.23...amplify-category-api@2.32.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* [#8223](https://github.com/aws-amplify/amplify-cli/issues/8223), conversion to typescript ([#8245](https://github.com/aws-amplify/amplify-cli/issues/8245)) ([096e6ca](https://github.com/aws-amplify/amplify-cli/commit/096e6ca19b94aa40ef249ea98d008380395afa16)) +* update dependency versions ([b4cbe97](https://github.com/aws-amplify/amplify-cli/commit/b4cbe97ea09f392469b4b84727db297228daf129)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* **amplify-provider-awscloudformation:** match env directive field for sandbox mode ([#3](https://github.com/aws-amplify/amplify-cli/issues/3)) ([3158549](https://github.com/aws-amplify/amplify-cli/commit/31585492fd4c358903d68d9640e9ac53e9b32eef)) +* **cli-api:** improve add and update api ([02af6b5](https://github.com/aws-amplify/amplify-cli/commit/02af6b582367b584ad755c889fb04722af355368)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [2.31.23](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-api@2.31.22...amplify-category-api@2.31.23) (2021-09-18) **Note:** Version bump only for package amplify-category-api diff --git a/packages/amplify-category-api/amplify-plugin.json b/packages/amplify-category-api/amplify-plugin.json index 8bd97a6a3c7..fe62d84d301 100644 --- a/packages/amplify-category-api/amplify-plugin.json +++ b/packages/amplify-category-api/amplify-plugin.json @@ -1,7 +1,7 @@ { "name": "api", "type": "category", - "commands": ["add-graphql-datasource", "add", "console", "gql-compile", "push", "rebuild", "remove", "update", "help"], + "commands": ["add-graphql-datasource", "add", "console", "gql-compile", "push", "remove", "update", "help"], "commandAliases": { "configure": "update" }, diff --git a/packages/amplify-category-api/package.json b/packages/amplify-category-api/package.json index 31940a9ea6e..2a5f6545363 100644 --- a/packages/amplify-category-api/package.json +++ b/packages/amplify-category-api/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-api", - "version": "2.31.23", + "version": "2.32.0-graphql-vnext-dev-preview.0", "description": "amplify-cli api plugin", "repository": { "type": "git", @@ -63,16 +63,15 @@ "@aws-cdk/region-info": "~1.124.0", "@graphql-tools/merge": "^6.0.18", "@octokit/rest": "^18.0.9", - "amplify-cli-core": "1.29.0", - "amplify-headless-interface": "1.10.0", - "amplify-prompts": "1.1.2", - "amplify-util-headless-input": "1.5.4", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", + "amplify-headless-interface": "1.11.0-graphql-vnext-dev-preview.0", + "amplify-util-headless-input": "1.5.5-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "constructs": "^3.3.125", "fs-extra": "^8.1.0", "graphql": "^14.5.8", - "graphql-relational-schema-transformer": "2.18.6", - "graphql-transformer-core": "6.29.7", + "graphql-relational-schema-transformer": "2.18.7-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "inquirer": "^7.3.3", "js-yaml": "^4.0.0", "lodash": "^4.17.21", diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-index.js.ejs b/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-index.js.ejs new file mode 100644 index 00000000000..20f0971452e --- /dev/null +++ b/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-index.js.ejs @@ -0,0 +1,25 @@ +// This is sample code. Please update this to suite your schema + +exports.handler = async (event) => { + console.log(`event >`, JSON.stringify(event, null, 2)); + const { + authorizationToken, + requestContext: { apiId, accountId }, + } = event; + const response = { + isAuthorized: authorizationToken === 'custom-authorized', + resolverContext: { + userid: 'user-id', + info: 'contextual information A', + more_info: 'contextual information B', + }, + deniedFields: [ + `arn:aws:appsync:${process.env.AWS_REGION}:${accountId}:apis/${apiId}/types/Event/fields/comments`, + `Mutation.createEvent`, + ], + ttlOverride: 300, + }; + console.log(`response >`, JSON.stringify(response, null, 2)); + return response; +}; + diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-package.json.ejs b/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-package.json.ejs new file mode 100644 index 00000000000..084c06bc3d0 --- /dev/null +++ b/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-package.json.ejs @@ -0,0 +1,6 @@ +{ + "name": "<%= props.functionName %>", + "version": "1.0.0", + "description": "Lambda function generated by Amplify for conflict handling", + "main": "index.js" +} diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-template.json.ejs b/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-template.json.ejs new file mode 100644 index 00000000000..acd908246dc --- /dev/null +++ b/packages/amplify-category-api/resources/awscloudformation/graphql-lambda-authorizer/graphql-lambda-authorizer-template.json.ejs @@ -0,0 +1,208 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Lambda resource stack creation using Amplify CLI", + "Parameters": { + "env": { + "Type": "String" + }<%if (props.dependsOn && props.dependsOn.length > 0) { %>,<% } %> + <% if (props.dependsOn) { %> + <% for(var i=0; i < props.dependsOn.length; i++) { %> + <% for(var j=0; j < props.dependsOn[i].attributes.length; j++) { %> + "<%= props.dependsOn[i].category %><%= props.dependsOn[i].resourceName %><%= props.dependsOn[i].attributes[j] %>": { + "Type": "String", + "Default": "<%= props.dependsOn[i].category %><%= props.dependsOn[i].resourceName %><%= props.dependsOn[i].attributes[j] %>" + }<%if (i !== props.dependsOn.length - 1 || j !== props.dependsOn[i].attributes.length - 1) { %>,<% } %> + <% } %> + <% } %> + <% } %> + }, + "Conditions": { + "ShouldNotCreateEnvResources": { + "Fn::Equals": [ + { + "Ref": "env" + }, + "NONE" + ] + } + }, + "Resources": { + "LambdaFunction": { + "Type": "AWS::Lambda::Function", + "Metadata": { + "aws:asset:path": "./src", + "aws:asset:property": "Code" + }, + "Properties": { + "Handler": "index.handler", + "FunctionName": { + "Fn::If": [ + "ShouldNotCreateEnvResources", + "<%= props.functionName %>", + { + + "Fn::Join": [ + "", + [ + "<%= props.functionName %>", + "-", + { + "Ref": "env" + } + ] + ] + } + ] + }, + "Environment": { + "Variables" : { + "ENV": { + "Ref": "env" + }, + "REGION": { + "Ref": "AWS::Region" + } + <% if (props.resourceProperties && props.resourceProperties.length > 0) { %>,<%- props.resourceProperties%> <% } %> + } + }, + "Role": { "Fn::GetAtt" : ["LambdaExecutionRole", "Arn"] }, + "Runtime": "nodejs14.x", + "Timeout": 25 + } + }, + "LambdaExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "RoleName": { + "Fn::If": [ + "ShouldNotCreateEnvResources", + "<%=props.roleName %>", + { + + "Fn::Join": [ + "", + [ + "<%=props.roleName %>", + "-", + { + "Ref": "env" + } + ] + ] + } + ] + }, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + } + } + } + ,"lambdaexecutionpolicy": { + "DependsOn": ["LambdaExecutionRole"], + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "lambda-execution-policy", + "Roles": [{ "Ref": "LambdaExecutionRole" }], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action":["logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents"], + "Resource": { "Fn::Sub" : [ "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", { "region": {"Ref": "AWS::Region"}, "account": {"Ref": "AWS::AccountId"}, "lambda": {"Ref": "LambdaFunction"}} ]} + }<% if (props.database && props.database.resourceName) { %>, + { + "Effect": "Allow", + "Action": ["dynamodb:GetItem","dynamodb:Query","dynamodb:Scan","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem"], + "Resource": [ + <% if (props.database && props.database.Arn) { %> + "<%= props.database.Arn %>", + { + "Fn::Join": [ + "/", + [ + "<%= props.database.Arn %>", + "index/*" + ] + ] + } + <% } else { %> + { "Ref": "storage<%= props.database.resourceName %>Arn" }, + { + "Fn::Join": [ + "/", + [ + { "Ref": "storage<%= props.database.resourceName %>Arn" }, + "index/*" + ] + ] + } + <% } %> + ] + } + <% } %> + ] + } + } + } + ,"PermissionForAppSyncToInvokeLambda": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "FunctionName": { + "Ref": "LambdaFunction" + }, + "Action": "lambda:InvokeFunction", + "Principal": "appsync.amazonaws.com" + } + } + <% if (props.categoryPolicies && props.categoryPolicies.length > 0 ) { %> + ,"AmplifyResourcesPolicy": { + "DependsOn": ["LambdaExecutionRole"], + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "amplify-lambda-execution-policy", + "Roles": [{ "Ref": "LambdaExecutionRole" }], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": <%- JSON.stringify(props.categoryPolicies) %> + } + } + } + <% } %> + }, + "Outputs": { + "Name": { + "Value": { + "Ref": "LambdaFunction" + } + }, + "Arn": { + "Value": {"Fn::GetAtt": ["LambdaFunction", "Arn"]} + }, + "Region": { + "Value": { + "Ref": "AWS::Region" + } + }, + "LambdaExecutionRole": { + "Value": { + "Ref": "LambdaExecutionRole" + } + } + } +} diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/blank-schema.graphql b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/blank-schema.graphql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/many-relationship-schema-v2.graphql b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/many-relationship-schema-v2.graphql new file mode 100644 index 00000000000..2619b187297 --- /dev/null +++ b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/many-relationship-schema-v2.graphql @@ -0,0 +1,18 @@ +type Blog @model { + id: ID! + name: String! + posts: [Post] @hasMany +} + +type Post @model { + id: ID! + title: String! + blog: Blog @belongsTo + comments: [Comment] @hasMany +} + +type Comment @model { + id: ID! + post: Post @belongsTo + content: String! +} diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/single-object-auth-schema-v2.graphql b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/single-object-auth-schema-v2.graphql new file mode 100644 index 00000000000..0faf0997d4a --- /dev/null +++ b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/single-object-auth-schema-v2.graphql @@ -0,0 +1,15 @@ +type Task + @model + @auth(rules: [ + { allow: groups, groups: ["Managers"], operations: [create, update, read, delete] } + { allow: groups, groups: ["Employees"], operations: [read] } ]) { + id: ID! + title: String! + description: String + status: String +} + +type PrivateNote @model @auth(rules: [{ allow: owner }]) { + id: ID! + content: String! +} diff --git a/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/single-object-schema-v2.graphql b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/single-object-schema-v2.graphql new file mode 100644 index 00000000000..74b097ceae9 --- /dev/null +++ b/packages/amplify-category-api/resources/awscloudformation/graphql-schemas/single-object-schema-v2.graphql @@ -0,0 +1,5 @@ +type Todo @model { + id: ID! + name: String! + description: String +} diff --git a/packages/amplify-category-api/src/__tests__/commands/api/rebuild.test.ts b/packages/amplify-category-api/src/__tests__/commands/api/rebuild.test.ts deleted file mode 100644 index ce28470ecd0..00000000000 --- a/packages/amplify-category-api/src/__tests__/commands/api/rebuild.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { $TSContext, FeatureFlags, stateManager } from 'amplify-cli-core'; -import { printer, prompter } from 'amplify-prompts'; -import { mocked } from 'ts-jest/utils'; -import { run } from '../../../commands/api/rebuild'; - -jest.mock('amplify-cli-core'); -jest.mock('amplify-prompts'); - -const FeatureFlags_mock = mocked(FeatureFlags); -const stateManager_mock = mocked(stateManager); -const printer_mock = mocked(printer); -const prompter_mock = mocked(prompter); - -FeatureFlags_mock.getBoolean.mockReturnValue(true); - -beforeEach(jest.clearAllMocks); - -const pushResourcesMock = jest.fn(); - -const context_stub = { - amplify: { - constructExeInfo: jest.fn(), - pushResources: pushResourcesMock, - }, - parameters: { - first: 'resourceName', - }, -} as unknown as $TSContext; - -it('prints error if iterative updates not enabled', async () => { - FeatureFlags_mock.getBoolean.mockReturnValueOnce(false); - - await run(context_stub); - - expect(printer_mock.error.mock.calls.length).toBe(1); - expect(pushResourcesMock.mock.calls.length).toBe(0); -}); - -it('exits early if no api in project', async () => { - stateManager_mock.getMeta.mockReturnValueOnce({ - api: {}, - }); - - await run(context_stub); - - expect(printer_mock.info.mock.calls.length).toBe(1); - expect(pushResourcesMock.mock.calls.length).toBe(0); -}); - -it('asks for strong confirmation before continuing', async () => { - stateManager_mock.getMeta.mockReturnValueOnce({ - api: { - testapiname: { - service: 'AppSync', - }, - }, - }); - - await run(context_stub); - - expect(prompter_mock.input.mock.calls.length).toBe(1); - expect(pushResourcesMock.mock.calls.length).toBe(1); - expect(pushResourcesMock.mock.calls[0][4]).toBe(true); // rebuild flag is set -}); diff --git a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/__snapshots__/cfn-api-artifact-handler.test.ts.snap b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/__snapshots__/cfn-api-artifact-handler.test.ts.snap index 36f39965a9b..3b9aa6928f1 100644 --- a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/__snapshots__/cfn-api-artifact-handler.test.ts.snap +++ b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/__snapshots__/cfn-api-artifact-handler.test.ts.snap @@ -18,6 +18,7 @@ Object { }, Object { "apiKeyConfig": Object { + "apiKeyExpirationDate": undefined, "apiKeyExpirationDays": undefined, "description": undefined, }, diff --git a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/cfn-api-artifact-handler.test.ts b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/cfn-api-artifact-handler.test.ts index 28e2aec71d3..e8dfa51770a 100644 --- a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/cfn-api-artifact-handler.test.ts +++ b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/cfn-api-artifact-handler.test.ts @@ -30,7 +30,7 @@ jest.mock('../../../provider-utils/awscloudformation/utils/amplify-meta-utils', jest.mock('amplify-cli-core'); -const fs_mock = (fs as unknown) as jest.Mocked; +const fs_mock = fs as unknown as jest.Mocked; const writeTransformerConfiguration_mock = writeTransformerConfiguration as jest.MockedFunction; const getAppSyncResourceName_mock = getAppSyncResourceName as jest.MockedFunction; const getAppSyncAuthConfig_mock = getAppSyncAuthConfig as jest.MockedFunction; diff --git a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/prompt-to-add-api-key.test.ts b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/prompt-to-add-api-key.test.ts new file mode 100644 index 00000000000..871b8c8f1ea --- /dev/null +++ b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/prompt-to-add-api-key.test.ts @@ -0,0 +1,44 @@ +import { $TSContext } from 'amplify-cli-core'; +import * as prompts from 'amplify-prompts'; +import { promptToAddApiKey } from '../../../provider-utils/awscloudformation/prompt-to-add-api-key'; +import * as walkthrough from '../../../provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough'; +import * as cfnApiArtifactHandler from '../../../provider-utils/awscloudformation/cfn-api-artifact-handler'; + +jest.mock('../../../provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough', () => ({ + askApiKeyQuestions: jest.fn(), +})); + +jest.mock('../../../provider-utils/awscloudformation/cfn-api-artifact-handler', () => ({ + getCfnApiArtifactHandler: jest.fn(() => { + return { updateArtifacts: jest.fn() }; + }), +})); + +jest.mock('amplify-prompts', () => ({ + prompter: { + confirmContinue: jest.fn().mockImplementation(() => true), + }, +})); + +describe('prompt to add Api Key', () => { + it('runs through expected user flow: print info, update files', async () => { + const envName = 'envone'; + const ctx = { + amplify: { + getEnvInfo() { + return { envName }; + }, + }, + } as unknown as $TSContext; + + jest.spyOn(prompts.prompter, 'confirmContinue'); + jest.spyOn(walkthrough, 'askApiKeyQuestions'); + jest.spyOn(cfnApiArtifactHandler, 'getCfnApiArtifactHandler'); + + await promptToAddApiKey(ctx); + + expect(prompts.prompter.confirmContinue).toHaveBeenCalledWith('Would you like to create an API Key?'); + expect(walkthrough.askApiKeyQuestions).toHaveBeenCalledTimes(1); + expect(cfnApiArtifactHandler.getCfnApiArtifactHandler).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.test.ts b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.test.ts index a664b0b4698..1d7ed55cdfd 100644 --- a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.test.ts +++ b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.test.ts @@ -3,7 +3,7 @@ import { askAdditionalAuthQuestions, } from '../../../../provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough'; import { authConfigHasApiKey, getAppSyncAuthConfig } from '../../../../provider-utils/awscloudformation/utils/amplify-meta-utils'; -import { FeatureFlags, CLIEnvironmentProvider, FeatureFlagRegistration } from 'amplify-cli-core'; +import { FeatureFlags } from 'amplify-cli-core'; jest.mock('../../../../provider-utils/awscloudformation/utils/amplify-meta-utils', () => ({ getAppSyncAuthConfig: jest.fn(), authConfigHasApiKey: jest.fn(), diff --git a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/__snapshots__/auth-config-to-app-sync-auth-type-bi-di-mapper.test.ts.snap b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/__snapshots__/auth-config-to-app-sync-auth-type-bi-di-mapper.test.ts.snap index 73049e4ca95..3c18c0fa241 100644 --- a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/__snapshots__/auth-config-to-app-sync-auth-type-bi-di-mapper.test.ts.snap +++ b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/__snapshots__/auth-config-to-app-sync-auth-type-bi-di-mapper.test.ts.snap @@ -12,6 +12,7 @@ Object { exports[`AppSyncAuthType to authConfig maps API_KEY correctly 1`] = ` Object { "apiKeyConfig": Object { + "apiKeyExpirationDate": undefined, "apiKeyExpirationDays": 120, "description": undefined, }, @@ -47,6 +48,7 @@ Object { exports[`authConfig to AppSyncAuthType maps API_KEY auth correctly 1`] = ` Object { + "apiKeyExpirationDate": undefined, "expirationTime": 120, "keyDescription": "api key description", "mode": "API_KEY", diff --git a/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/global-sandbox-mode.test.ts b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/global-sandbox-mode.test.ts new file mode 100644 index 00000000000..a895da7f667 --- /dev/null +++ b/packages/amplify-category-api/src/__tests__/provider-utils/awscloudformation/utils/global-sandbox-mode.test.ts @@ -0,0 +1,21 @@ +import { defineGlobalSandboxMode } from '../../../../provider-utils/awscloudformation/utils/global-sandbox-mode'; +import { $TSContext } from 'amplify-cli-core'; + +describe('global sandbox mode GraphQL directive', () => { + it('returns AMPLIFY_DIRECTIVE type with code comment, directive, and env name', () => { + const envName = 'envone'; + const ctx = <$TSContext>{ + amplify: { + getEnvInfo() { + return { envName }; + }, + }, + }; + + expect(defineGlobalSandboxMode(ctx)) + .toBe(`# This allows public create, read, update, and delete access for a limited time to all models via API Key. +# To configure PRODUCTION-READY authorization rules, review: https://docs.amplify.aws/cli/graphql-transformer/auth +type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: \"${envName}\") # FOR TESTING ONLY!\n +`); + }); +}); diff --git a/packages/amplify-category-api/src/commands/api.js b/packages/amplify-category-api/src/commands/api.js index e8c261e00df..948dc4ae4a2 100644 --- a/packages/amplify-category-api/src/commands/api.js +++ b/packages/amplify-category-api/src/commands/api.js @@ -41,11 +41,6 @@ module.exports = { name: 'console', description: 'Opens the web console for the selected api service', }, - { - name: 'rebuild', - description: - 'Removes and recreates all DynamoDB tables backing a GraphQL API. Useful for resetting test data during the development phase of an app', - }, ]; context.amplify.showHelp(header, commands); diff --git a/packages/amplify-category-api/src/commands/api/rebuild.ts b/packages/amplify-category-api/src/commands/api/rebuild.ts deleted file mode 100644 index 2173ba77c49..00000000000 --- a/packages/amplify-category-api/src/commands/api/rebuild.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { $TSContext, FeatureFlags, stateManager } from 'amplify-cli-core'; -import { printer, prompter, exact } from 'amplify-prompts'; - -const subcommand = 'rebuild'; -const category = 'api'; - -export const name = subcommand; - -const rebuild = true; - -export const run = async (context: $TSContext) => { - if (!FeatureFlags.getBoolean('graphqlTransformer.enableIterativeGSIUpdates')) { - printer.error('Iterative GSI Updates must be enabled to rebuild an API. See https://docs.amplify.aws/cli/reference/feature-flags/'); - return; - } - const apiNames = Object.entries(stateManager.getMeta()?.api || {}) - .filter(([_, meta]) => (meta as any).service === 'AppSync') - .map(([name]) => name); - if (apiNames.length === 0) { - printer.info('No GraphQL API configured in the project. Only GraphQL APIs can be rebuilt. To add a GraphQL API run `amplify add api`.'); - return; - } - if (apiNames.length > 1) { - // this condition should never hit as we have upstream defensive logic to prevent multiple GraphQL APIs. But just to cover all the bases - printer.error( - 'You have multiple GraphQL APIs in the project. Only one GraphQL API is allowed per project. Run `amplify remove api` to remove an API.', - ); - return; - } - const apiName = apiNames[0]; - printer.warn(`This will recreate all tables backing models in your GraphQL API ${apiName}.`); - printer.warn('ALL EXISTING DATA IN THESE TABLES WILL BE LOST.'); - await prompter.input('Type the name of the API to confirm you want to continue', { - validate: exact(apiName, 'Input does not match the GraphQL API name'), - }); - const { amplify, parameters } = context; - const resourceName = parameters.first; - amplify.constructExeInfo(context); - return amplify.pushResources(context, category, resourceName, undefined, rebuild); -}; diff --git a/packages/amplify-category-api/src/index.ts b/packages/amplify-category-api/src/index.ts index af7f1625011..3593ba6b977 100644 --- a/packages/amplify-category-api/src/index.ts +++ b/packages/amplify-category-api/src/index.ts @@ -3,6 +3,9 @@ import fs from 'fs-extra'; import path from 'path'; import { run } from './commands/api/console'; import { getCfnApiArtifactHandler } from './provider-utils/awscloudformation/cfn-api-artifact-handler'; +import { askAuthQuestions } from './provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough'; +import { getAppSyncResourceName, getAppSyncAuthConfig } from './provider-utils/awscloudformation//utils/amplify-meta-utils'; +import { authConfigToAppSyncAuthType } from './provider-utils/awscloudformation/utils/auth-config-to-app-sync-auth-type-bi-di-mapper'; export { NETWORK_STACK_LOGICAL_ID } from './category-constants'; export { DEPLOYMENT_MECHANISM } from './provider-utils/awscloudformation/base-api-stack'; @@ -15,6 +18,7 @@ export { processDockerConfig, } from './provider-utils/awscloudformation/utils/containers-artifacts'; export { getContainers } from './provider-utils/awscloudformation/docker-compose'; +export { promptToAddApiKey } from './provider-utils/awscloudformation/prompt-to-add-api-key'; const category = 'api'; @@ -217,3 +221,32 @@ export async function handleAmplifyEvent(context, args) { context.print.info(`${category} handleAmplifyEvent to be implemented`); context.print.info(`Received event args ${args}`); } + +export async function addGraphQLAuthorizationMode(context, args) { + const { authType, printLeadText, authSettings } = args; + const apiName = getAppSyncResourceName(context.amplify.getProjectMeta()); + if (!apiName) { + return; + } + + const authConfig = getAppSyncAuthConfig(context.amplify.getProjectMeta()); + const addAuthConfig = await askAuthQuestions(authType, context, printLeadText, authSettings); + authConfig.additionalAuthenticationProviders.push(addAuthConfig); + await context.amplify.updateamplifyMetaAfterResourceUpdate(category, apiName, 'output', { authConfig }); + await context.amplify.updateBackendConfigAfterResourceUpdate(category, apiName, 'output', { authConfig }); + + await getCfnApiArtifactHandler(context).updateArtifacts( + { + version: 1, + serviceModification: { + serviceName: 'AppSync', + additionalAuthTypes: authConfig.additionalAuthenticationProviders.map(authConfigToAppSyncAuthType), + }, + }, + { + skipCompile: false, + }, + ); + + return addAuthConfig; +} \ No newline at end of file diff --git a/packages/amplify-category-api/src/provider-utils/api-artifact-handler.ts b/packages/amplify-category-api/src/provider-utils/api-artifact-handler.ts index a3b7de27c51..7dbab41d779 100644 --- a/packages/amplify-category-api/src/provider-utils/api-artifact-handler.ts +++ b/packages/amplify-category-api/src/provider-utils/api-artifact-handler.ts @@ -1,6 +1,10 @@ import { AddApiRequest, UpdateApiRequest } from 'amplify-headless-interface'; +export interface ApiArtifactHandlerOptions { + skipCompile?: boolean; +} + export interface ApiArtifactHandler { createArtifacts(request: AddApiRequest): Promise; - updateArtifacts(request: UpdateApiRequest): Promise; + updateArtifacts(request: UpdateApiRequest, opts?: ApiArtifactHandlerOptions): Promise; } diff --git a/packages/amplify-category-api/src/provider-utils/awscloudformation/cfn-api-artifact-handler.ts b/packages/amplify-category-api/src/provider-utils/awscloudformation/cfn-api-artifact-handler.ts index cc421715b5d..f06fc279414 100644 --- a/packages/amplify-category-api/src/provider-utils/awscloudformation/cfn-api-artifact-handler.ts +++ b/packages/amplify-category-api/src/provider-utils/awscloudformation/cfn-api-artifact-handler.ts @@ -12,7 +12,7 @@ import _ from 'lodash'; import * as path from 'path'; import uuid from 'uuid'; import { category } from '../../category-constants'; -import { ApiArtifactHandler } from '../api-artifact-handler'; +import { ApiArtifactHandler, ApiArtifactHandlerOptions } from '../api-artifact-handler'; import { cfnParametersFilename, gqlSchemaFilename, provider, rootAssetDir } from './aws-constants'; import { authConfigHasApiKey, checkIfAuthExists, getAppSyncAuthConfig, getAppSyncResourceName } from './utils/amplify-meta-utils'; import { appSyncAuthTypeToAuthConfig } from './utils/auth-config-to-app-sync-auth-type-bi-di-mapper'; @@ -96,7 +96,7 @@ class CfnApiArtifactHandler implements ApiArtifactHandler { // TODO once the AddApiRequest contains multiple services this class should depend on an ApiArtifactHandler // for each service and delegate to the correct one - updateArtifacts = async (request: UpdateApiRequest): Promise => { + updateArtifacts = async (request: UpdateApiRequest, opts?: ApiArtifactHandlerOptions): Promise => { const updates = request.serviceModification; const apiName = getAppSyncResourceName(this.context.amplify.getProjectMeta()); if (!apiName) { @@ -118,11 +118,14 @@ class CfnApiArtifactHandler implements ApiArtifactHandler { if (updates.additionalAuthTypes) { authConfig.additionalAuthenticationProviders = updates.additionalAuthTypes.map(appSyncAuthTypeToAuthConfig); } - await this.context.amplify.executeProviderUtils(this.context, 'awscloudformation', 'compileSchema', { - resourceDir, - parameters: this.getCfnParameters(apiName, authConfig, resourceDir), - authConfig, - }); + + if (!opts?.skipCompile) { + await this.context.amplify.executeProviderUtils(this.context, 'awscloudformation', 'compileSchema', { + resourceDir, + parameters: this.getCfnParameters(apiName, authConfig, resourceDir), + authConfig, + }); + } this.context.amplify.updateamplifyMetaAfterResourceUpdate(category, apiName, 'output', { authConfig }); this.context.amplify.updateBackendConfigAfterResourceUpdate(category, apiName, 'output', { authConfig }); diff --git a/packages/amplify-category-api/src/provider-utils/awscloudformation/prompt-to-add-api-key.ts b/packages/amplify-category-api/src/provider-utils/awscloudformation/prompt-to-add-api-key.ts new file mode 100644 index 00000000000..4e3a92b5ea4 --- /dev/null +++ b/packages/amplify-category-api/src/provider-utils/awscloudformation/prompt-to-add-api-key.ts @@ -0,0 +1,27 @@ +import { $TSContext } from 'amplify-cli-core'; +import { askApiKeyQuestions } from './service-walkthroughs/appSync-walkthrough'; +import { authConfigToAppSyncAuthType } from './utils/auth-config-to-app-sync-auth-type-bi-di-mapper'; +import { getCfnApiArtifactHandler } from './cfn-api-artifact-handler'; +import { prompter } from 'amplify-prompts'; + +export async function promptToAddApiKey(context: $TSContext): Promise { + if (await prompter.confirmContinue('Would you like to create an API Key?')) { + const apiKeyConfig = await askApiKeyQuestions(); + const authConfig = [apiKeyConfig]; + + await getCfnApiArtifactHandler(context).updateArtifacts( + { + version: 1, + serviceModification: { + serviceName: 'AppSync', + additionalAuthTypes: authConfig.map(authConfigToAppSyncAuthType), + }, + }, + { + skipCompile: true, + }, + ); + + return apiKeyConfig; + } +} diff --git a/packages/amplify-category-api/src/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.ts b/packages/amplify-category-api/src/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.ts index f2878dbb503..94f15b58361 100644 --- a/packages/amplify-category-api/src/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.ts +++ b/packages/amplify-category-api/src/provider-utils/awscloudformation/service-walkthroughs/appSync-walkthrough.ts @@ -3,13 +3,15 @@ import { dataStoreLearnMore } from '../sync-conflict-handler-assets/syncAssets'; import inquirer from 'inquirer'; import fs from 'fs-extra'; import path from 'path'; -import { rootAssetDir } from '../aws-constants'; -import { collectDirectivesByTypeNames, readProjectConfiguration, ConflictHandlerType } from 'graphql-transformer-core'; +import { rootAssetDir, provider } from '../aws-constants'; +import { collectDirectivesByTypeNames, readProjectConfiguration } from 'graphql-transformer-core'; import { category } from '../../../category-constants'; import { UpdateApiRequest } from '../../../../../amplify-headless-interface/lib/interface/api/update'; import { authConfigToAppSyncAuthType } from '../utils/auth-config-to-app-sync-auth-type-bi-di-mapper'; import { resolverConfigToConflictResolution } from '../utils/resolver-config-to-conflict-resolution-bi-di-mapper'; import _ from 'lodash'; +import chalk from 'chalk'; +import uuid from 'uuid'; import { getAppSyncAuthConfig, checkIfAuthExists, authConfigHasApiKey } from '../utils/amplify-meta-utils'; import { ResourceAlreadyExistsError, @@ -21,12 +23,17 @@ import { $TSContext, open, } from 'amplify-cli-core'; +import { Duration, Expiration } from '@aws-cdk/core'; +import { defineGlobalSandboxMode } from '../utils/global-sandbox-mode'; const serviceName = 'AppSync'; const elasticContainerServiceName = 'ElasticContainer'; const providerName = 'awscloudformation'; const graphqlSchemaDir = path.join(rootAssetDir, 'graphql-schemas'); +// keep in sync with ServiceName in amplify-category-function, but probably it will not change +const FunctionServiceNameLambdaFunction = 'Lambda'; + const authProviderChoices = [ { name: 'API key', @@ -46,6 +53,63 @@ const authProviderChoices = [ }, ]; +const conflictResolutionHanlderChoices = [ + { + name: 'Auto Merge', + value: 'AUTOMERGE', + }, + { + name: 'Optimistic Concurrency', + value: 'OPTIMISTIC_CONCURRENCY', + }, + { + name: 'Custom Lambda', + value: 'LAMBDA', + }, + { + name: 'Learn More', + value: 'Learn More', + }, +]; + +const schemaTemplatesV1 = [ + { + name: 'Single object with fields (e.g., “Todo” with ID, name, description)', + value: 'single-object-schema.graphql', + }, + { + name: 'One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)', + value: 'many-relationship-schema.graphql', + }, + { + name: 'Objects with fine-grained access control (e.g., a project management app with owner-based authorization)', + value: 'single-object-auth-schema.graphql', + }, + { + name: 'Blank Schema', + value: 'blank-schema.graphql', + }, +]; + +const schemaTemplatesV2 = [ + { + name: 'Single object with fields (e.g., “Todo” with ID, name, description)', + value: 'single-object-schema-v2.graphql', + }, + { + name: 'One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)', + value: 'many-relationship-schema-v2.graphql', + }, + { + name: 'Objects with fine-grained access control (e.g., a project management app with owner-based authorization)', + value: 'single-object-auth-schema-v2.graphql', + }, + { + name: 'Blank Schema', + value: 'blank-schema.graphql', + }, +]; + export const openConsole = async (context: $TSContext) => { const amplifyMeta = stateManager.getMeta(); const categoryAmplifyMeta = amplifyMeta[category]; @@ -136,90 +200,129 @@ export const openConsole = async (context: $TSContext) => { } }; -export const serviceWalkthrough = async (context: $TSContext, defaultValuesFilename, serviceMetadata) => { - const resourceName = resourceAlreadyExists(context); +const serviceApiInputWalkthrough = async (context: $TSContext, defaultValuesFilename, serviceMetadata) => { + let continuePrompt = false; let authConfig; let defaultAuthType; let resolverConfig; - - if (resourceName) { - const errMessage = - 'You already have an AppSync API in your project. Use the "amplify update api" command to update your existing AppSync API.'; - context.print.warning(errMessage); - await context.usageData.emitError(new ResourceAlreadyExistsError(errMessage)); - exitOnNextTick(0); - } - const { amplify } = context; const { inputs } = serviceMetadata; const defaultValuesSrc = `${__dirname}/../default-values/${defaultValuesFilename}`; const { getAllDefaults } = require(defaultValuesSrc); const allDefaultValues = getAllDefaults(amplify.getProjectDetails()); - const resourceQuestions = [ - { - type: inputs[1].type, - name: inputs[1].key, - message: inputs[1].question, - validate: amplify.inputValidation(inputs[1]), - default: () => { - const defaultValue = allDefaultValues[inputs[1].key]; - return defaultValue; + let resourceAnswers = {}; + resourceAnswers[inputs[1].key] = allDefaultValues[inputs[1].key]; + resourceAnswers[inputs[0].key] = resourceAnswers[inputs[1].key]; + + // + // Default authConfig - API Key (expires in 7 days) + // + authConfig = { + defaultAuthentication: { + apiKeyConfig: { + apiKeyExpirationDays: 7, }, + authenticationType: 'API_KEY', }, - ]; + additionalAuthenticationProviders: [], + }; - // API name question + // + // Repeat prompt until user selects Continue + // + while (!continuePrompt) { + const getAuthModeChoice = async () => { + if (authConfig.defaultAuthentication.authenticationType === 'API_KEY') { + return `${ + authProviderChoices.find(choice => choice.value === authConfig.defaultAuthentication.authenticationType).name + } (default, expiration time: ${authConfig.defaultAuthentication.apiKeyConfig.apiKeyExpirationDays} days from now)`; + } + return `${authProviderChoices.find(choice => choice.value === authConfig.defaultAuthentication.authenticationType).name} (default)`; + }; - const resourceAnswers = await inquirer.prompt(resourceQuestions); - resourceAnswers[inputs[0].key] = resourceAnswers[inputs[1].key]; + const getAdditionalAuthModeChoices = async () => { + let additionalAuthModesText = ''; + authConfig.additionalAuthenticationProviders.map(async authMode => { + additionalAuthModesText += `, ${authProviderChoices.find(choice => choice.value === authMode.authenticationType).name}`; + }); + return additionalAuthModesText; + }; - // Ask additonal questions + const basicInfoQuestionChoices = []; - ({ authConfig, defaultAuthType } = await askDefaultAuthQuestion(context)); - ({ authConfig, resolverConfig } = await askAdditionalQuestions(context, authConfig, defaultAuthType)); + basicInfoQuestionChoices.push({ + name: chalk`{bold Name:} ${resourceAnswers[inputs[1].key]}`, + value: 'API_NAME', + }); - // Ask schema file question + basicInfoQuestionChoices.push({ + name: chalk`{bold Authorization modes:} ${await getAuthModeChoice()}${await getAdditionalAuthModeChoices()}`, + value: 'API_AUTH_MODE', + }); - const schemaFileQuestion = { - type: inputs[2].type, - name: inputs[2].key, - message: inputs[2].question, - validate: amplify.inputValidation(inputs[2]), - default: () => { - const defaultValue = allDefaultValues[inputs[2].key]; - return defaultValue; - }, - }; + basicInfoQuestionChoices.push({ + name: chalk`{bold Conflict detection (required for DataStore):} ${resolverConfig?.project ? 'Enabled' : 'Disabled'}`, + value: 'CONFLICT_DETECTION', + }); - const schemaFileAnswer = await inquirer.prompt(schemaFileQuestion); + if (resolverConfig?.project) { + basicInfoQuestionChoices.push({ + name: chalk`{bold Conflict resolution strategy:} ${ + conflictResolutionHanlderChoices.find(x => x.value === resolverConfig.project.ConflictHandler).name + }`, + value: 'CONFLICT_STRATEGY', + }); + } - let schemaContent = ''; - let askToEdit = true; - if (schemaFileAnswer[inputs[2].key]) { - // User has an annotated schema file - const filePathQuestion = { - type: inputs[3].type, - name: inputs[3].key, - message: inputs[3].question, - validate: amplify.inputValidation(inputs[3]), - }; - const { schemaFilePath } = await inquirer.prompt(filePathQuestion); - schemaContent = fs.readFileSync(schemaFilePath, 'utf8'); - askToEdit = false; - } else { - // Schema template selection - const templateSelectionQuestion = { - type: inputs[4].type, - name: inputs[4].key, - message: inputs[4].question, - choices: inputs[4].options.filter(templateSchemaFilter(authConfig)), - validate: amplify.inputValidation(inputs[4]), + basicInfoQuestionChoices.push({ + name: 'Continue', + value: 'CONTINUE', + }); + + const basicInfoQuestion = { + type: 'list', + name: 'basicApiSettings', + message: 'Here is the GraphQL API that we will create. Select a setting to edit or continue', + default: 'CONTINUE', + choices: basicInfoQuestionChoices, }; - const { templateSelection } = await inquirer.prompt(templateSelectionQuestion); - const schemaFilePath = path.join(graphqlSchemaDir, templateSelection); - schemaContent = fs.readFileSync(schemaFilePath, 'utf8'); + let { basicApiSettings } = await inquirer.prompt([basicInfoQuestion]); + + switch (basicApiSettings) { + case 'API_NAME': + const resourceQuestions = [ + { + type: inputs[1].type, + name: inputs[1].key, + message: inputs[1].question, + validate: amplify.inputValidation(inputs[1]), + default: () => { + const defaultValue = allDefaultValues[inputs[1].key]; + return defaultValue; + }, + }, + ]; + // API name question + resourceAnswers = await inquirer.prompt(resourceQuestions); + resourceAnswers[inputs[0].key] = resourceAnswers[inputs[1].key]; + break; + case 'API_AUTH_MODE': + // Ask additonal questions + ({ authConfig, defaultAuthType } = await askDefaultAuthQuestion(context)); + ({ authConfig } = await askAdditionalQuestions(context, authConfig, defaultAuthType)); + break; + case 'CONFLICT_DETECTION': + resolverConfig = await askResolverConflictQuestion(context, resolverConfig); + break; + case 'CONFLICT_STRATEGY': + resolverConfig = await askResolverConflictHandlerQuestion(context); + break; + case 'CONTINUE': + continuePrompt = true; + break; + } } return { @@ -227,8 +330,100 @@ export const serviceWalkthrough = async (context: $TSContext, defaultValuesFilen output: { authConfig, }, - noCfnFile: true, resolverConfig, + }; +}; + +const updateApiInputWalkthrough = async (context, project, resolverConfig, modelTypes) => { + let authConfig; + let defaultAuthType; + const updateChoices = [ + { + name: 'Authorization modes', + value: 'AUTH_MODE', + }, + ]; + // check if DataStore is enabled for the entire API + if (project.config && !_.isEmpty(project.config.ResolverConfig)) { + updateChoices.push({ + name: 'Conflict resolution strategy', + value: 'CONFLICT_STRATEGY', + }); + updateChoices.push({ + name: 'Disable conflict detection', + value: 'DISABLE_CONFLICT', + }); + } else { + updateChoices.push({ + name: 'Enable conflict detection (required for DataStore)', + value: 'ENABLE_CONFLICT', + }); + } + + const updateOptionQuestion = { + type: 'list', + name: 'updateOption', + message: 'Select a setting to edit', + choices: updateChoices, + }; + + const { updateOption } = await inquirer.prompt([updateOptionQuestion]); + + if (updateOption === 'ENABLE_CONFLICT') { + resolverConfig = await askResolverConflictHandlerQuestion(context, modelTypes); + } else if (updateOption === 'DISABLE_CONFLICT') { + resolverConfig = {}; + } else if (updateOption === 'AUTH_MODE') { + ({ authConfig, defaultAuthType } = await askDefaultAuthQuestion(context)); + authConfig = await askAdditionalAuthQuestions(context, authConfig, defaultAuthType); + } else if (updateOption === 'CONFLICT_STRATEGY') { + resolverConfig = await askResolverConflictHandlerQuestion(context, modelTypes); + } + + return { + authConfig, + resolverConfig, + }; +}; + +export const serviceWalkthrough = async (context: $TSContext, defaultValuesFilename, serviceMetadata) => { + const resourceName = resourceAlreadyExists(context); + const useExperimentalPipelineTransformer = FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer'); + await addLambdaAuthorizerChoice(); + + if (resourceName) { + const errMessage = + 'You already have an AppSync API in your project. Use the "amplify update api" command to update your existing AppSync API.'; + context.print.warning(errMessage); + await context.usageData.emitError(new ResourceAlreadyExistsError(errMessage)); + exitOnNextTick(0); + } + + const { amplify } = context; + const { inputs } = serviceMetadata; + + const basicInfoAnswers = await serviceApiInputWalkthrough(context, defaultValuesFilename, serviceMetadata); + let schemaContent = ''; + let askToEdit = true; + + // Schema template selection + const schemaTemplateOptions = useExperimentalPipelineTransformer ? schemaTemplatesV2 : schemaTemplatesV1; + const templateSelectionQuestion = { + type: inputs[4].type, + name: inputs[4].key, + message: inputs[4].question, + choices: schemaTemplateOptions.filter(templateSchemaFilter(basicInfoAnswers.output.authConfig)), + validate: amplify.inputValidation(inputs[4]), + }; + + const { templateSelection } = await inquirer.prompt(templateSelectionQuestion); + const schemaFilePath = path.join(graphqlSchemaDir, templateSelection); + schemaContent += useExperimentalPipelineTransformer ? defineGlobalSandboxMode(context) : ''; + schemaContent += fs.readFileSync(schemaFilePath, 'utf8'); + + return { + ...basicInfoAnswers, + noCfnFile: true, schemaContent, askToEdit, }; @@ -238,13 +433,14 @@ export const updateWalkthrough = async (context): Promise => { const { allResources } = await context.amplify.getResourceStatus(); let resourceDir; let resourceName; + let resource; let authConfig; - let defaultAuthType; const resources = allResources.filter(resource => resource.service === 'AppSync'); + await addLambdaAuthorizerChoice(); // There can only be one appsync resource if (resources.length > 0) { - const resource = resources[0]; + resource = resources[0]; if (resource.providerPlugin !== providerName) { // TODO: Move message string to seperate file throw new Error( @@ -265,6 +461,8 @@ export const updateWalkthrough = async (context): Promise => { const project = await readProjectConfiguration(resourceDir); let resolverConfig = project.config.ResolverConfig; + await displayApiInformation(context, resource, project); + // Check for common errors const directiveMap = collectDirectivesByTypeNames(project.schema); let modelTypes = []; @@ -276,45 +474,8 @@ export const updateWalkthrough = async (context): Promise => { } }); } - const updateChoices = [ - { - name: 'Walkthrough all configurations', - value: 'all', - }, - { - name: 'Update auth settings', - value: 'authUpdate', - }, - ]; - // check if DataStore is enabled for the entire API - if (project.config && !_.isEmpty(project.config.ResolverConfig)) { - updateChoices.push({ name: 'Disable DataStore for entire API', value: 'disableDatastore' }); - } else { - updateChoices.push({ name: 'Enable DataStore for entire API', value: 'enableDatastore' }); - } - const updateOptionQuestion = { - type: 'list', - name: 'updateOption', - message: 'Select from the options below', - choices: updateChoices, - }; - - const { updateOption } = await inquirer.prompt([updateOptionQuestion]); - - if (updateOption === 'enableDatastore') { - resolverConfig = { - project: { ConflictHandler: ConflictHandlerType.AUTOMERGE, ConflictDetection: 'VERSION' }, - }; - } else if (updateOption === 'disableDatastore') { - resolverConfig = {}; - } else if (updateOption === 'authUpdate') { - ({ authConfig, defaultAuthType } = await askDefaultAuthQuestion(context)); - authConfig = await askAdditionalAuthQuestions(context, authConfig, defaultAuthType); - } else if (updateOption === 'all') { - ({ authConfig, defaultAuthType } = await askDefaultAuthQuestion(context)); - ({ authConfig, resolverConfig } = await askAdditionalQuestions(context, authConfig, defaultAuthType, modelTypes)); - } + ({ authConfig, resolverConfig } = await updateApiInputWalkthrough(context, project, resolverConfig, modelTypes)); return { version: 1, @@ -330,111 +491,130 @@ export const updateWalkthrough = async (context): Promise => { }; }; -async function askAdditionalQuestions(context, authConfig, defaultAuthType, modelTypes?) { - let resolverConfig; +async function displayApiInformation(context, resource, project) { + let authModes: string[] = []; + authModes.push( + `- Default: ${await displayAuthMode(context, resource, resource.output.authConfig.defaultAuthentication.authenticationType)}`, + ); + await resource.output.authConfig.additionalAuthenticationProviders.map(async authMode => { + authModes.push(`- ${await displayAuthMode(context, resource, authMode.authenticationType)}`); + }); - const advancedSettingsQuestion = { - type: 'list', - name: 'advancedSettings', - message: 'Do you want to configure advanced settings for the GraphQL API', - choices: [ - { - name: 'No, I am done.', - value: false, - }, - { - name: 'Yes, I want to make some additional changes.', - value: true, - }, - ], - }; + context.print.info(''); - const advancedSettingsAnswer = await inquirer.prompt([advancedSettingsQuestion]); + context.print.success('General information'); + context.print.info('- Name: '.concat(resource.resourceName)); + if (resource?.output?.GraphQLAPIEndpointOutput) { + context.print.info(`- API endpoint: ${resource?.output?.GraphQLAPIEndpointOutput}`); + } + context.print.info(''); - if (advancedSettingsAnswer.advancedSettings) { - authConfig = await askAdditionalAuthQuestions(context, authConfig, defaultAuthType); - resolverConfig = await askResolverConflictQuestion(context, modelTypes); + context.print.success('Authorization modes'); + authModes.forEach(authMode => context.print.info(authMode)); + context.print.info(''); + + context.print.success('Conflict detection (required for DataStore)'); + if (project.config && !_.isEmpty(project.config.ResolverConfig)) { + context.print.info( + `- Conflict resolution strategy: ${ + conflictResolutionHanlderChoices.find(choice => choice.value === project.config.ResolverConfig.project.ConflictHandler).name + }`, + ); + } else { + context.print.info('- Disabled'); } - return { authConfig, resolverConfig }; + context.print.info(''); } -async function askResolverConflictQuestion(context, modelTypes?) { - let resolverConfig: any = {}; +async function displayAuthMode(context, resource, authMode) { + if (authMode == 'API_KEY' && resource.output.GraphQLAPIKeyOutput) { + let { apiKeys } = await context.amplify.executeProviderUtils(context, 'awscloudformation', 'getAppSyncApiKeys', { + apiId: resource.output.GraphQLAPIIdOutput, + }); + let apiKeyExpires = apiKeys.find(key => key.id == resource.output.GraphQLAPIKeyOutput)?.expires; + if (!apiKeyExpires) { + return authProviderChoices.find(choice => choice.value === authMode).name; + } + let apiKeyExpiresDate = new Date(apiKeyExpires * 1000); + return `${authProviderChoices.find(choice => choice.value === authMode).name} expiring ${apiKeyExpiresDate}: ${ + resource.output.GraphQLAPIKeyOutput + }`; + } + return authProviderChoices.find(choice => choice.value === authMode).name; +} - if (await context.prompt.confirm('Enable conflict detection?')) { - const askConflictResolutionStrategy = async msg => { - let conflictResolutionStrategy; - - do { - const conflictResolutionQuestion: ListQuestion = { - type: 'list', - name: 'conflictResolutionStrategy', - message: msg, - default: 'AUTOMERGE', - choices: [ - { - name: 'Auto Merge', - value: 'AUTOMERGE', - }, - { - name: 'Optimistic Concurrency', - value: 'OPTIMISTIC_CONCURRENCY', - }, - { - name: 'Custom Lambda', - value: 'LAMBDA', - }, - { - name: 'Learn More', - value: 'Learn More', - }, - ], - }; - if (conflictResolutionStrategy === 'Learn More') { - conflictResolutionQuestion.prefix = dataStoreLearnMore; - } - ({ conflictResolutionStrategy } = await inquirer.prompt([conflictResolutionQuestion])); - } while (conflictResolutionStrategy === 'Learn More'); +async function askAdditionalQuestions(context, authConfig, defaultAuthType, modelTypes?) { + authConfig = await askAdditionalAuthQuestions(context, authConfig, defaultAuthType); + return { authConfig }; +} - let syncConfig: any = { - ConflictHandler: conflictResolutionStrategy, - ConflictDetection: 'VERSION', - }; +async function askResolverConflictQuestion(context, resolverConfig, modelTypes?) { + let resolverConfigResponse: any = {}; - if (conflictResolutionStrategy === 'LAMBDA') { - const { newFunction, lambdaFunctionName } = await askSyncFunctionQuestion(context); - syncConfig.LambdaConflictHandler = { - name: lambdaFunctionName, - new: newFunction, - }; + if (await context.prompt.confirm('Enable conflict detection?', !resolverConfig?.project)) { + resolverConfigResponse = await askResolverConflictHandlerQuestion(context, modelTypes); + } + + return resolverConfigResponse; +} + +async function askResolverConflictHandlerQuestion(context, modelTypes?) { + let resolverConfig: any = {}; + const askConflictResolutionStrategy = async msg => { + let conflictResolutionStrategy; + + do { + const conflictResolutionQuestion: ListQuestion = { + type: 'list', + name: 'conflictResolutionStrategy', + message: msg, + default: 'AUTOMERGE', + choices: conflictResolutionHanlderChoices, + }; + if (conflictResolutionStrategy === 'Learn More') { + conflictResolutionQuestion.prefix = dataStoreLearnMore; } + ({ conflictResolutionStrategy } = await inquirer.prompt([conflictResolutionQuestion])); + } while (conflictResolutionStrategy === 'Learn More'); - return syncConfig; + let syncConfig: any = { + ConflictHandler: conflictResolutionStrategy, + ConflictDetection: 'VERSION', }; - resolverConfig.project = await askConflictResolutionStrategy('Select the default resolution strategy'); + if (conflictResolutionStrategy === 'LAMBDA') { + const { newFunction, lambdaFunctionName } = await askSyncFunctionQuestion(context); + syncConfig.LambdaConflictHandler = { + name: lambdaFunctionName, + new: newFunction, + }; + } - // Ask for per-model resolver override setting + return syncConfig; + }; - if (modelTypes && modelTypes.length > 0) { - if (await context.prompt.confirm('Do you want to override default per model settings?', false)) { - const modelTypeQuestion = { - type: 'checkbox', - name: 'selectedModelTypes', - message: 'Select the models from below:', - choices: modelTypes, - }; + resolverConfig.project = await askConflictResolutionStrategy('Select the default resolution strategy'); - const { selectedModelTypes } = await inquirer.prompt([modelTypeQuestion]); + // Ask for per-model resolver override setting - if (selectedModelTypes.length > 0) { - resolverConfig.models = {}; - for (let i = 0; i < selectedModelTypes.length; i += 1) { - resolverConfig.models[selectedModelTypes[i]] = await askConflictResolutionStrategy( - `Select the resolution strategy for ${selectedModelTypes[i]} model`, - ); - } + if (modelTypes && modelTypes.length > 0) { + if (await context.prompt.confirm('Do you want to override default per model settings?', false)) { + const modelTypeQuestion = { + type: 'checkbox', + name: 'selectedModelTypes', + message: 'Select the models from below:', + choices: modelTypes, + }; + + const { selectedModelTypes } = await inquirer.prompt([modelTypeQuestion]); + + if (selectedModelTypes.length > 0) { + resolverConfig.models = {}; + for (let i = 0; i < selectedModelTypes.length; i += 1) { + resolverConfig.models[selectedModelTypes[i]] = await askConflictResolutionStrategy( + `Select the resolution strategy for ${selectedModelTypes[i]} model`, + ); } } } @@ -477,10 +657,23 @@ async function askSyncFunctionQuestion(context) { return { newFunction, lambdaFunctionName }; } + +async function addLambdaAuthorizerChoice() { + const useExperimentalPipelineTransformer = FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer'); + if (useExperimentalPipelineTransformer && !authProviderChoices.find(choice => choice.value == 'AWS_LAMBDA')) { + authProviderChoices.push({ + name: 'Lambda', + value: 'AWS_LAMBDA', + }); + } +} + async function askDefaultAuthQuestion(context) { + await addLambdaAuthorizerChoice(); const currentAuthConfig = getAppSyncAuthConfig(context.amplify.getProjectMeta()); const currentDefaultAuth = currentAuthConfig && currentAuthConfig.defaultAuthentication ? currentAuthConfig.defaultAuthentication.authenticationType : undefined; + const defaultAuthTypeQuestion = { type: 'list', name: 'defaultAuthType', @@ -492,7 +685,7 @@ async function askDefaultAuthQuestion(context) { const { defaultAuthType } = await inquirer.prompt([defaultAuthTypeQuestion]); // Get default auth configured - const defaultAuth = await askAuthQuestions(defaultAuthType, context); + const defaultAuth = await askAuthQuestions(defaultAuthType, context, false, currentAuthConfig?.defaultAuthentication); return { authConfig: { @@ -508,9 +701,11 @@ export async function askAdditionalAuthQuestions(context, authConfig, defaultAut if (await context.prompt.confirm('Configure additional auth types?')) { // Get additional auth configured const remainingAuthProviderChoices = authProviderChoices.filter(p => p.value !== defaultAuthType); - const currentAdditionalAuth = ((currentAuthConfig && currentAuthConfig.additionalAuthenticationProviders - ? currentAuthConfig.additionalAuthenticationProviders - : []) as any[]).map(authProvider => authProvider.authenticationType); + const currentAdditionalAuth = ( + (currentAuthConfig && currentAuthConfig.additionalAuthenticationProviders + ? currentAuthConfig.additionalAuthenticationProviders + : []) as any[] + ).map(authProvider => authProvider.authenticationType); const additionalProvidersQuestion: CheckboxQuestion = { type: 'checkbox', @@ -525,7 +720,12 @@ export async function askAdditionalAuthQuestions(context, authConfig, defaultAut for (let i = 0; i < additionalProvidersAnswer.authType.length; i += 1) { const authProvider = additionalProvidersAnswer.authType[i]; - const config = await askAuthQuestions(authProvider, context, true); + const config = await askAuthQuestions( + authProvider, + context, + true, + currentAuthConfig?.additionalAuthenticationProviders?.find(authSetting => authSetting.authenticationType == authProvider), + ); authConfig.additionalAuthenticationProviders.push(config); } @@ -537,7 +737,7 @@ export async function askAdditionalAuthQuestions(context, authConfig, defaultAut return authConfig; } -async function askAuthQuestions(authType, context, printLeadText = false) { +export async function askAuthQuestions(authType, context, printLeadText = false, authSettings) { if (authType === 'AMAZON_COGNITO_USER_POOLS') { if (printLeadText) { context.print.info('Cognito UserPool configuration'); @@ -553,7 +753,7 @@ async function askAuthQuestions(authType, context, printLeadText = false) { context.print.info('API key configuration'); } - const apiKeyConfig = await askApiKeyQuestions(); + const apiKeyConfig = await askApiKeyQuestions(authSettings); return apiKeyConfig; } @@ -569,11 +769,21 @@ async function askAuthQuestions(authType, context, printLeadText = false) { context.print.info('OpenID Connect configuration'); } - const openIDConnectConfig = await askOpenIDConnectQuestions(); + const openIDConnectConfig = await askOpenIDConnectQuestions(authSettings); return openIDConnectConfig; } + if (authType === 'AWS_LAMBDA') { + if (printLeadText) { + context.print.info('Lambda Authorizer configuration'); + } + + const lambdaConfig = await askLambdaQuestion(context); + + return lambdaConfig; + } + const errMessage = `Unknown authType: ${authType}`; context.print.error(errMessage); await context.usageData.emitError(new UnknownResourceTypeError(errMessage)); @@ -582,9 +792,8 @@ async function askAuthQuestions(authType, context, printLeadText = false) { async function askUserPoolQuestions(context) { let authResourceName = checkIfAuthExists(context); - if (!authResourceName) { - authResourceName = await context.amplify.invokePluginMethod(context, 'auth', undefined, 'add', [context]); + authResourceName = await context.amplify.invokePluginMethod(context, 'auth', undefined, 'add', [context, true]); } else { context.print.info('Use a Cognito user pool configured as a part of this project.'); } @@ -600,18 +809,25 @@ async function askUserPoolQuestions(context) { }; } -async function askApiKeyQuestions() { +export async function askApiKeyQuestions(authSettings = undefined) { + let defaultValues = { + apiKeyExpirationDays: 7, + description: undefined, + }; + Object.assign(defaultValues, authSettings?.apiKeyConfig); + const apiKeyQuestions = [ { type: 'input', name: 'description', message: 'Enter a description for the API key:', + default: defaultValues.description, }, { type: 'input', name: 'apiKeyExpirationDays', message: 'After how many days from now the API key should expire (1-365):', - default: 7, + default: defaultValues.apiKeyExpirationDays, validate: validateDays, // adding filter to ensure parsing input as int -> https://github.com/SBoudrias/Inquirer.js/issues/866 filter: value => (isNaN(parseInt(value, 10)) ? value : parseInt(value, 10)), @@ -619,6 +835,8 @@ async function askApiKeyQuestions() { ]; const apiKeyConfig = await inquirer.prompt(apiKeyQuestions); + const apiKeyExpirationDaysNum = Number(apiKeyConfig.apiKeyExpirationDays); + apiKeyConfig.apiKeyExpirationDate = Expiration.after(Duration.days(apiKeyExpirationDaysNum)).date; return { authenticationType: 'API_KEY', @@ -626,35 +844,49 @@ async function askApiKeyQuestions() { }; } -async function askOpenIDConnectQuestions() { +async function askOpenIDConnectQuestions(authSettings) { + let defaultValues = { + authTTL: undefined, + clientId: undefined, + iatTTL: undefined, + issuerUrl: undefined, + name: undefined, + }; + Object.assign(defaultValues, authSettings?.openIDConnectConfig); + const openIDConnectQuestions = [ { type: 'input', name: 'name', message: 'Enter a name for the OpenID Connect provider:', + default: defaultValues.name, }, { type: 'input', name: 'issuerUrl', message: 'Enter the OpenID Connect provider domain (Issuer URL):', validate: validateIssuerUrl, + default: defaultValues.issuerUrl, }, { type: 'input', name: 'clientId', message: 'Enter the Client Id from your OpenID Client Connect application (optional):', + default: defaultValues.clientId, }, { type: 'input', name: 'iatTTL', message: 'Enter the number of milliseconds a token is valid after being issued to a user:', validate: validateTTL, + default: defaultValues.iatTTL, }, { type: 'input', name: 'authTTL', message: 'Enter the number of milliseconds a token is valid after being authenticated:', validate: validateTTL, + default: defaultValues.authTTL, }, ]; @@ -677,9 +909,10 @@ function validateDays(input) { } function validateIssuerUrl(input) { - const isValid = /^(((?!http:\/\/(?!localhost))([a-zA-Z0-9.]{1,}):\/\/([a-zA-Z0-9-._~:?#@!$&'()*+,;=/]{1,})\/)|(?!http)(?!https)([a-zA-Z0-9.]{1,}):\/\/)$/.test( - input, - ); + const isValid = + /^(((?!http:\/\/(?!localhost))([a-zA-Z0-9.]{1,}):\/\/([a-zA-Z0-9-._~:?#@!$&'()*+,;=/]{1,})\/)|(?!http)(?!https)([a-zA-Z0-9.]{1,}):\/\/)$/.test( + input, + ); if (!isValid) { return 'The value must be a valid URI with a trailing forward slash. HTTPS must be used instead of HTTP unless you are using localhost.'; @@ -779,8 +1012,8 @@ const buildPolicyResource = (resourceName: string, path: string | null) => { { Ref: `${category}${resourceName}GraphQLAPIIdOutput`, }, - ...(path ? [path] : []) - ] + ...(path ? [path] : []), + ], ], }; }; @@ -788,7 +1021,8 @@ const buildPolicyResource = (resourceName: string, path: string | null) => { const templateSchemaFilter = authConfig => { const authIncludesCognito = getAuthTypes(authConfig).includes('AMAZON_COGNITO_USER_POOLS'); return (templateOption: ListChoiceOptions): boolean => - authIncludesCognito || templateOption.value !== 'single-object-auth-schema.graphql'; + authIncludesCognito || + templateOption.name !== 'Objects with fine-grained access control (e.g., a project management app with owner-based authorization)'; }; const getAuthTypes = authConfig => { @@ -800,3 +1034,160 @@ const getAuthTypes = authConfig => { return [...uniqueAuthTypes.keys()]; }; + +async function askLambdaQuestion(context) { + const existingFunctions = functionsExist(context); + const choices = [ + { + name: 'Create a new Lambda function', + value: 'newFunction', + }, + ]; + if (existingFunctions) { + choices.push({ + name: 'Use a Lambda function already added in the current Amplify project', + value: 'projectFunction', + }); + } + + let defaultFunctionType = 'newFunction'; + const lambdaAnswer = await inquirer.prompt({ + name: 'functionType', + type: 'list', + message: 'Choose a Lambda source', + choices, + default: defaultFunctionType, + }); + + const { lambdaFunction } = await askLambdaSource(context, lambdaAnswer.functionType); + const { ttlSeconds } = await inquirer.prompt({ + type: 'input', + name: 'ttlSeconds', + message: 'How long should the authorization response be cached in seconds?', + validate: validateTTL, + default: 300, + }); + + const lambdaAuthorizerConfig = { + lambdaFunction, + ttlSeconds, + } + + return { + authenticationType: 'AWS_LAMBDA', + lambdaAuthorizerConfig, + }; +} + +function functionsExist(context) { + if (!context.amplify.getProjectDetails().amplifyMeta.function) { + return false; + } + + const functionResources = context.amplify.getProjectDetails().amplifyMeta.function; + const lambdaFunctions = []; + Object.keys(functionResources).forEach(resourceName => { + if (functionResources[resourceName].service === FunctionServiceNameLambdaFunction) { + lambdaFunctions.push(resourceName); + } + }); + + if (lambdaFunctions.length === 0) { + return false; + } + + return true; +} + +async function askLambdaSource(context, functionType) { + switch (functionType) { + case 'projectFunction': + return askLambdaFromProject(context); + case 'newFunction': + return newLambdaFunction(context); + default: + throw new Error('Type not supported'); + } +} + +async function newLambdaFunction(context) { + // const resourceName = await context.amplify.invokePluginMethod(context, 'function', undefined, 'add', [ + // context, + // 'awscloudformation', + // FunctionServiceNameLambdaFunction, + // ]); + const resourceName = await createLambdaAuthorizerFunction(context); + + // context.print.success('Succesfully added the Lambda function locally'); + + return { lambdaFunction: resourceName }; +} + +async function askLambdaFromProject(context) { + const functionResources = context.amplify.getProjectDetails().amplifyMeta.function; + const lambdaFunctions = []; + Object.keys(functionResources).forEach(resourceName => { + if (functionResources[resourceName].service === FunctionServiceNameLambdaFunction) { + lambdaFunctions.push(resourceName); + } + }); + + const answer = await inquirer.prompt({ + name: 'lambdaFunction', + type: 'list', + message: 'Choose one of the Lambda function', + choices: lambdaFunctions, + default: lambdaFunctions[0], + }); + + await context.amplify.invokePluginMethod(context, 'function', undefined, 'addAppSyncInvokeMethodPermission', [ + answer.lambdaFunction, + ]); + + return { lambdaFunction: answer.lambdaFunction }; +} + +async function createLambdaAuthorizerFunction(context) { + const targetDir = context.amplify.pathManager.getBackendDirPath(); + const assetDir = path.normalize(path.join(rootAssetDir, 'graphql-lambda-authorizer')); + const [shortId] = uuid().split('-'); + + const functionName = `graphQlLambdaAuthorizer${shortId}`; + + const functionProps = { + functionName: `${functionName}`, + roleName: `${functionName}LambdaRole`, + }; + + const copyJobs = [ + { + dir: assetDir, + template: 'graphql-lambda-authorizer-index.js.ejs', + target: `${targetDir}/function/${functionName}/src/index.js`, + }, + { + dir: assetDir, + template: 'graphql-lambda-authorizer-package.json.ejs', + target: `${targetDir}/function/${functionName}/src/package.json`, + }, + { + dir: assetDir, + template: 'graphql-lambda-authorizer-template.json.ejs', + target: `${targetDir}/function/${functionName}/${functionName}-cloudformation-template.json`, + }, + ]; + + // copy over the files + await context.amplify.copyBatch(context, copyJobs, functionProps, true); + + const backendConfigs = { + service: FunctionServiceNameLambdaFunction, + providerPlugin: provider, + build: true, + }; + + await context.amplify.updateamplifyMetaAfterResourceAdd('function', functionName, backendConfigs); + context.print.success(`Successfully added ${functionName} function locally`); + + return functionName; +}; \ No newline at end of file diff --git a/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/auth-config-to-app-sync-auth-type-bi-di-mapper.ts b/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/auth-config-to-app-sync-auth-type-bi-di-mapper.ts index d52094eae50..948b72a4957 100644 --- a/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/auth-config-to-app-sync-auth-type-bi-di-mapper.ts +++ b/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/auth-config-to-app-sync-auth-type-bi-di-mapper.ts @@ -3,6 +3,7 @@ import { AppSyncAPIKeyAuthType, AppSyncCognitoUserPoolsAuthType, AppSyncOpenIDConnectAuthType, + AppSyncLambdaAuthType, } from 'amplify-headless-interface'; import _ from 'lodash'; @@ -30,6 +31,7 @@ const authConfigToAppSyncAuthTypeMap: Record AppSyn API_KEY: authConfig => ({ mode: 'API_KEY', expirationTime: authConfig.apiKeyConfig.apiKeyExpirationDays, + apiKeyExpirationDate: authConfig.apiKeyConfig?.apiKeyExpirationDate, keyDescription: authConfig.apiKeyConfig.description, }), AWS_IAM: () => ({ @@ -47,6 +49,11 @@ const authConfigToAppSyncAuthTypeMap: Record AppSyn openIDAuthTTL: authConfig.openIDConnectConfig.authTTL, openIDIatTTL: authConfig.openIDConnectConfig.iatTTL, }), + AWS_LAMBDA: authConfig => ({ + mode: 'AWS_LAMBDA', + lambdaFunction: authConfig.lambdaAuthorizerConfig.lambdaFunction, + ttlSeconds: authConfig.lambdaAuthorizerConfig.ttlSeconds, + }), }; const appSyncAuthTypeToAuthConfigMap: Record any> = { @@ -54,6 +61,7 @@ const appSyncAuthTypeToAuthConfigMap: Record ({ + authenticationType: 'AWS_LAMBDA', + lambdaAuthorizerConfig: { + lambdaFunction: authType.lambdaFunction, + ttlSeconds: authType.ttlSeconds, + }, + }), }; diff --git a/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/edit-schema-flow.ts b/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/edit-schema-flow.ts index 96dbb2acfd8..11964238731 100644 --- a/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/edit-schema-flow.ts +++ b/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/edit-schema-flow.ts @@ -8,7 +8,7 @@ export const editSchemaFlow = async (context: any, apiName: string) => { type: 'confirm', name: 'editNow', message: 'Do you want to edit the schema now?', - default: false, + default: true, }; if (!(await inquirer.prompt(prompt)).editNow) return; diff --git a/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/global-sandbox-mode.ts b/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/global-sandbox-mode.ts new file mode 100644 index 00000000000..27fc26f1f30 --- /dev/null +++ b/packages/amplify-category-api/src/provider-utils/awscloudformation/utils/global-sandbox-mode.ts @@ -0,0 +1,10 @@ +import { $TSContext } from 'amplify-cli-core'; + +export function defineGlobalSandboxMode(context: $TSContext): string { + const envName = context.amplify.getEnvInfo().envName; + + return `# This allows public create, read, update, and delete access for a limited time to all models via API Key. +# To configure PRODUCTION-READY authorization rules, review: https://docs.amplify.aws/cli/graphql-transformer/auth +type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: \"${envName}\") # FOR TESTING ONLY!\n +`; +} diff --git a/packages/amplify-category-api/src/provider-utils/supported-services.ts b/packages/amplify-category-api/src/provider-utils/supported-services.ts index 9bf9cbba01e..d5fe5482aa2 100644 --- a/packages/amplify-category-api/src/provider-utils/supported-services.ts +++ b/packages/amplify-category-api/src/provider-utils/supported-services.ts @@ -52,6 +52,10 @@ export const supportedServices = { name: 'Objects with fine-grained access control (e.g., a project management app with owner-based authorization)', value: 'single-object-auth-schema.graphql', }, + { + name: 'Blank Schema', + value: 'blank-schema.graphql', + }, ], required: true, }, diff --git a/packages/amplify-category-api/tsconfig.json b/packages/amplify-category-api/tsconfig.json index 31c85559bbb..dd332b004a4 100644 --- a/packages/amplify-category-api/tsconfig.json +++ b/packages/amplify-category-api/tsconfig.json @@ -14,9 +14,7 @@ "src/__tests__" ], "references": [ - {"path": "../amplify-cli-core"}, {"path": "../amplify-headless-interface"}, - {"path": "../amplify-prompts"}, {"path": "../graphql-transformer-core"}, {"path": "../amplify-util-headless-input"}, ] diff --git a/packages/amplify-category-auth/CHANGELOG.md b/packages/amplify-category-auth/CHANGELOG.md index 6ec9fe340a3..9bed6be2492 100644 --- a/packages/amplify-category-auth/CHANGELOG.md +++ b/packages/amplify-category-auth/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.38.2-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-auth@2.38.1...amplify-category-auth@2.38.2-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* add missing await, fix import paths ([#8199](https://github.com/aws-amplify/amplify-cli/issues/8199)) ([51c4dd9](https://github.com/aws-amplify/amplify-cli/commit/51c4dd9c021d894fe2c06fc005e1e1960fe4529c)) +* **amplify-category-auth:** update front end config on pull ([#8173](https://github.com/aws-amplify/amplify-cli/issues/8173)) ([da2b008](https://github.com/aws-amplify/amplify-cli/commit/da2b0083add2f5b10520efade8628080a34c8791)) + + + + + ## [2.38.1](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-auth@2.38.0...amplify-category-auth@2.38.1) (2021-09-20) diff --git a/packages/amplify-category-auth/package.json b/packages/amplify-category-auth/package.json index 4a8265e158a..ac35fc45f83 100644 --- a/packages/amplify-category-auth/package.json +++ b/packages/amplify-category-auth/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-auth", - "version": "2.38.1", + "version": "2.38.2-graphql-vnext-dev-preview.0", "description": "amplify-cli authentication plugin", "repository": { "type": "git", @@ -23,10 +23,10 @@ "test-watch": "jest --watch" }, "dependencies": { - "amplify-cli-core": "1.29.0", - "amplify-headless-interface": "1.10.0", - "amplify-util-headless-input": "1.5.4", - "amplify-util-import": "1.5.12", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", + "amplify-headless-interface": "1.11.0-graphql-vnext-dev-preview.0", + "amplify-util-headless-input": "1.5.5-graphql-vnext-dev-preview.0", + "amplify-util-import": "1.5.13-graphql-vnext-dev-preview.0", "aws-cdk": "~1.124.0", "aws-sdk": "^2.963.0", "chalk": "^4.1.1", diff --git a/packages/amplify-category-auth/src/index.js b/packages/amplify-category-auth/src/index.js index 60d30618107..7912c91db6b 100644 --- a/packages/amplify-category-auth/src/index.js +++ b/packages/amplify-category-auth/src/index.js @@ -34,7 +34,7 @@ const { } = require('./provider-utils/awscloudformation/utils/auth-sms-workflow-helper'); // this function is being kept for temporary compatability. -async function add(context) { +async function add(context, skipNextSteps = false) { const { amplify } = context; const servicesMetadata = getSupportedServices(); const existingAuth = amplify.getProjectDetails().amplifyMeta.auth || {}; @@ -54,7 +54,7 @@ async function add(context) { context.print.error('Provider not configured for this category'); return; } - return providerController.addResource(context, result.service); + return providerController.addResource(context, result.service, skipNextSteps); }) .catch(err => { context.print.info(err.stack); diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts index 8986123be4b..f40a03833c0 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts @@ -11,7 +11,7 @@ import { doesConfigurationIncludeSMS } from '../utils/auth-sms-workflow-helper'; * The consumer returns the resourceName of the generated resource. * @param context The amplify context */ -export const getAddAuthHandler = (context: any) => async (request: ServiceQuestionsResult) => { +export const getAddAuthHandler = (context: any, skipNextSteps: boolean = false) => async (request: ServiceQuestionsResult) => { const serviceMetadata = getSupportedServices()[request.serviceName]; const { cfnFilename, defaultValuesFilename, provider } = serviceMetadata; @@ -26,7 +26,7 @@ export const getAddAuthHandler = (context: any) => async (request: ServiceQuesti await getPostAddAuthMetaUpdater(context, { service: requestWithDefaults.serviceName, providerName: provider })( requestWithDefaults.resourceName!, ); - await getPostAddAuthMessagePrinter(context.print)(requestWithDefaults.resourceName!); + await getPostAddAuthMessagePrinter(context.print)(requestWithDefaults.resourceName!, skipNextSteps); if (doesConfigurationIncludeSMS(request)) { await printSMSSandboxWarning(context.print); diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js index 6f1e6260f2c..3a8a5691e93 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js @@ -14,10 +14,10 @@ function serviceQuestions(context, defaultValuesFilename, stringMapsFilename, se return serviceWalkthrough(context, defaultValuesFilename, stringMapsFilename, serviceMetadata); } -async function addResource(context, service) { +async function addResource(context, service, skipNextSteps = false) { const serviceMetadata = getSupportedServices()[service]; const { defaultValuesFilename, stringMapsFilename, serviceWalkthroughFilename } = serviceMetadata; - return getAddAuthHandler(context)( + return getAddAuthHandler(context, skipNextSteps)( await serviceQuestions(context, defaultValuesFilename, stringMapsFilename, serviceWalkthroughFilename, serviceMetadata), ); } diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts index b5c0c0148a8..9b3f9a71bae 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts @@ -5,9 +5,12 @@ import { BannerMessage } from 'amplify-cli-core'; * A factory function that returns a function that prints the "success message" after adding auth * @param print The amplify print object */ -export const getPostAddAuthMessagePrinter = (print: any) => (resourceName: string) => { +export const getPostAddAuthMessagePrinter = (print: any) => (resourceName: string, skipNextSteps: boolean = false) => { print.success(`Successfully added auth resource ${resourceName} locally`); - printCommonText(print); + + if (!skipNextSteps) { + printCommonText(print); + } }; /** diff --git a/packages/amplify-category-function/CHANGELOG.md b/packages/amplify-category-function/CHANGELOG.md index db371a3f98d..9b27c41a294 100644 --- a/packages/amplify-category-function/CHANGELOG.md +++ b/packages/amplify-category-function/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.35.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-function@2.34.7...amplify-category-function@2.35.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [2.34.7](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-function@2.34.6...amplify-category-function@2.34.7) (2021-09-18) **Note:** Version bump only for package amplify-category-function diff --git a/packages/amplify-category-function/package.json b/packages/amplify-category-function/package.json index 5b9d38614c0..7b29d42f804 100644 --- a/packages/amplify-category-function/package.json +++ b/packages/amplify-category-function/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-function", - "version": "2.34.7", + "version": "2.35.0-graphql-vnext-dev-preview.0", "description": "amplify-cli function plugin", "repository": { "type": "git", @@ -22,7 +22,7 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", "archiver": "^5.3.0", "aws-sdk": "^2.963.0", @@ -32,7 +32,7 @@ "folder-hash": "^4.0.1", "fs-extra": "^8.1.0", "globby": "^11.0.3", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "inquirer": "^7.3.3", "inquirer-datepicker": "^2.0.0", "jstreemap": "^1.28.2", diff --git a/packages/amplify-category-function/src/index.ts b/packages/amplify-category-function/src/index.ts index 63a6841431e..8ed85797560 100644 --- a/packages/amplify-category-function/src/index.ts +++ b/packages/amplify-category-function/src/index.ts @@ -27,8 +27,7 @@ export { lambdasWithApiDependency } from './provider-utils/awscloudformation/uti export { hashLayerResource } from './provider-utils/awscloudformation/utils/layerHelpers'; export { migrateLegacyLayer } from './provider-utils/awscloudformation/utils/layerMigrationUtils'; export { packageResource } from './provider-utils/awscloudformation/utils/package'; -export { updateDependentFunctionsCfn } from './provider-utils/awscloudformation/utils/updateDependentFunctionCfn'; -export { loadFunctionParameters } from './provider-utils/awscloudformation/utils/loadFunctionParameters'; +export { updateDependentFunctionsCfn, addAppSyncInvokeMethodPermission } from './provider-utils/awscloudformation/utils/updateDependentFunctionCfn'; export async function add(context, providerName, service, parameters) { const options = { diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/updateDependentFunctionCfn.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/updateDependentFunctionCfn.ts index 896f75a930e..e655d5afbcf 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/updateDependentFunctionCfn.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/updateDependentFunctionCfn.ts @@ -1,4 +1,4 @@ -import { $TSContext, $TSObject, JSONUtilities } from 'amplify-cli-core'; +import { $TSAny, $TSContext, $TSObject, JSONUtilities, pathManager } from 'amplify-cli-core'; import { FunctionParameters } from 'amplify-function-plugin-interface'; import { getResourcesForCfn, generateEnvVariablesForCfn } from '../service-walkthroughs/execPermissionsWalkthrough'; import { updateCFNFileForResourcePermissions } from '../service-walkthroughs/lambda-walkthrough'; @@ -83,3 +83,27 @@ export async function updateDependentFunctionsCfn( context.amplify.updateamplifyMetaAfterResourceUpdate(categoryName, lambda.resourceName, 'dependsOn', lambda.dependsOn); } } + +export function addAppSyncInvokeMethodPermission( + functionName: string, +) { + const backendDir = pathManager.getBackendDirPath(); + const resourceDirPath = path.join(backendDir, categoryName, functionName); + const cfnFileName = `${functionName}-cloudformation-template.json`; + const cfnFilePath = path.join(resourceDirPath, cfnFileName); + const cfnContent = JSONUtilities.readJson<$TSAny>(cfnFilePath); + + if (!cfnContent.Resources.PermissionForAppSyncToInvokeLambda) { + cfnContent.Resources.PermissionForAppSyncToInvokeLambda = { + Type: 'AWS::Lambda::Permission', + Properties: { + FunctionName: { + Ref: "LambdaFunction" + }, + Action: "lambda:InvokeFunction", + Principal: "appsync.amazonaws.com", + }, + }; + } + JSONUtilities.writeJson(cfnFilePath, cfnContent); +} \ No newline at end of file diff --git a/packages/amplify-category-geo/CHANGELOG.md b/packages/amplify-category-geo/CHANGELOG.md index e4d87c4d45c..eb62117caff 100644 --- a/packages/amplify-category-geo/CHANGELOG.md +++ b/packages/amplify-category-geo/CHANGELOG.md @@ -2,3 +2,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 1.0.1-graphql-vnext-dev-preview.0 (2021-09-27) + + +### Bug Fixes + +* add missing await, fix import paths ([#8199](https://github.com/aws-amplify/amplify-cli/issues/8199)) ([51c4dd9](https://github.com/aws-amplify/amplify-cli/commit/51c4dd9c021d894fe2c06fc005e1e1960fe4529c)) diff --git a/packages/amplify-category-geo/amplify-plugin.json b/packages/amplify-category-geo/amplify-plugin.json index c0e36ecff65..24b496cf3ff 100644 --- a/packages/amplify-category-geo/amplify-plugin.json +++ b/packages/amplify-category-geo/amplify-plugin.json @@ -12,5 +12,5 @@ "commandAliases":{ "configure": "update" }, - "eventHandlers": [] + "eventHandlers": ["PrePush"] } \ No newline at end of file diff --git a/packages/amplify-category-geo/package.json b/packages/amplify-category-geo/package.json index 5653c43499b..854375f4d96 100644 --- a/packages/amplify-category-geo/package.json +++ b/packages/amplify-category-geo/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-geo", - "version": "1.0.0", + "version": "1.0.1-graphql-vnext-dev-preview.0", "description": "Amplify CLI plugin to manage the Geo resources for the project", "repository": { "type": "git", @@ -22,8 +22,11 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", - "amplify-prompts": "1.1.2", + "@aws-cdk/aws-iam": "~1.124.0", + "@aws-cdk/aws-location": "~1.124.0", + "@aws-cdk/core": "~1.124.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", + "amplify-prompts": "1.2.0-graphql-vnext-dev-preview.0", "folder-hash": "^4.0.1", "fs-extra": "^8.1.0", "globby": "^11.0.3", @@ -31,10 +34,7 @@ "lodash": "^4.17.19", "mime-types": "^2.1.26", "promise-sequential": "^1.1.1", - "uuid": "^8.3.2", - "@aws-cdk/core": "~1.124.0", - "@aws-cdk/aws-location": "~1.124.0", - "@aws-cdk/aws-iam": "~1.124.0" + "uuid": "^8.3.2" }, "jest": { "collectCoverage": true, diff --git a/packages/amplify-category-geo/src/__tests__/commands/geo/remove.test.ts b/packages/amplify-category-geo/src/__tests__/commands/geo/remove.test.ts index 893509abd8d..2d613d44045 100644 --- a/packages/amplify-category-geo/src/__tests__/commands/geo/remove.test.ts +++ b/packages/amplify-category-geo/src/__tests__/commands/geo/remove.test.ts @@ -59,15 +59,4 @@ describe('remove command tests', () => { expect(mockRemoveResource).toHaveBeenCalledWith(mockContext, service); }); - - it('remove resource workflow is not invoked for unsupported region', async() => { - mockAmplifyMeta.providers[provider] = { - Region: 'eu-west-2' - }; - stateManager.getMeta = jest.fn().mockReturnValue(mockAmplifyMeta); - - await run(mockContext); - - expect(mockRemoveResource).toBeCalledTimes(0); - }); }); diff --git a/packages/amplify-category-geo/src/commands/geo/remove.ts b/packages/amplify-category-geo/src/commands/geo/remove.ts index 73c82f4d697..2bf435392b1 100644 --- a/packages/amplify-category-geo/src/commands/geo/remove.ts +++ b/packages/amplify-category-geo/src/commands/geo/remove.ts @@ -4,17 +4,12 @@ import { supportedServices } from '../../supportedServices'; import { $TSAny, $TSContext } from 'amplify-cli-core'; import { removeResource } from '../../provider-controllers'; import { printer } from 'amplify-prompts'; -import { verifySupportedRegion } from '../../service-utils/resourceUtils'; export const name = 'remove'; export const run = async(context: $TSContext) => { const { amplify } = context; try { - if(!verifySupportedRegion()) { - return; - } - const result: {service: string, providerName: string} = await amplify.serviceSelectionPrompt(context, category, supportedServices, chooseServiceMessageRemove); if (result.providerName !== provider) { diff --git a/packages/amplify-category-geo/src/index.ts b/packages/amplify-category-geo/src/index.ts index 5c9ab4f8556..359fdfe7c88 100644 --- a/packages/amplify-category-geo/src/index.ts +++ b/packages/amplify-category-geo/src/index.ts @@ -1,11 +1,11 @@ -import { $TSContext, $TSObject, stateManager } from 'amplify-cli-core'; +import { $TSContext, $TSObject, stateManager, exitOnNextTick, $TSAny } from 'amplify-cli-core'; import { category } from './constants'; import * as addCommand from './commands/geo/add'; import * as updateCommand from './commands/geo/update'; import * as removeCommand from './commands/geo/remove'; import * as consoleCommand from './commands/geo/console'; import * as helpCommand from './commands/geo/help'; -import { getServicePermissionPolicies } from './service-utils/resourceUtils'; +import { getServicePermissionPolicies, verifySupportedRegion, checkAnyGeoResourceExists } from './service-utils/resourceUtils'; import { ServiceName } from './service-utils/constants'; import { printer } from 'amplify-prompts'; @@ -32,9 +32,19 @@ export const executeAmplifyCommand = async (context: $TSContext) => { } }; -export const handleAmplifyEvent = (context: $TSContext, args: $TSObject) => { - printer.info(`${category} handleAmplifyEvent to be implemented`); - printer.info(`Received event args ${args}`); +export const handleAmplifyEvent = async (context: $TSContext, args: $TSAny) => { + switch (args.event) { + case 'PrePush': + if((await checkAnyGeoResourceExists()) && !verifySupportedRegion()) { + const errMessage = 'Failed to create Geo resources'; + await context.usageData.emitError(new Error(errMessage)); + printer.info('Remove Geo resources using "amplify remove geo" and retry "amplify push"'); + exitOnNextTick(1); + } + break; + default: + break; + } }; export const getPermissionPolicies = (context: $TSContext, resourceOpsMapping: $TSObject) => { diff --git a/packages/amplify-category-geo/src/service-utils/resourceUtils.ts b/packages/amplify-category-geo/src/service-utils/resourceUtils.ts index 5fb1104509c..b026ab269ba 100644 --- a/packages/amplify-category-geo/src/service-utils/resourceUtils.ts +++ b/packages/amplify-category-geo/src/service-utils/resourceUtils.ts @@ -219,8 +219,16 @@ export const getServicePermissionPolicies = ( export const verifySupportedRegion = (): boolean => { const currentRegion = stateManager.getMeta()?.providers[provider]?.Region; if(!supportedRegions.includes(currentRegion)) { - printer.error(`Geo category is not supported in your region: ${currentRegion}`); + printer.error(`Geo category is not supported in the region: [${currentRegion}]`); return false; } return true; }; + +/** + * Check if any Geo resource exists + */ + export const checkAnyGeoResourceExists = async (): Promise => { + const geoMeta = stateManager.getMeta()?.[category]; + return geoMeta && Object.keys(geoMeta) && Object.keys(geoMeta).length > 0; +} diff --git a/packages/amplify-category-hosting/CHANGELOG.md b/packages/amplify-category-hosting/CHANGELOG.md index 04ccf5b9bf1..6e990c655bb 100644 --- a/packages/amplify-category-hosting/CHANGELOG.md +++ b/packages/amplify-category-hosting/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.7.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-hosting@2.7.21...amplify-category-hosting@2.7.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-category-hosting + + + + + ## [2.7.21](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-hosting@2.7.20...amplify-category-hosting@2.7.21) (2021-09-18) **Note:** Version bump only for package amplify-category-hosting diff --git a/packages/amplify-category-hosting/package.json b/packages/amplify-category-hosting/package.json index bd3d9a08ff5..a86949acccb 100644 --- a/packages/amplify-category-hosting/package.json +++ b/packages/amplify-category-hosting/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-hosting", - "version": "2.7.21", + "version": "2.7.22-graphql-vnext-dev-preview.0", "description": "amplify-cli hosting plugin", "repository": { "type": "git", @@ -18,7 +18,7 @@ "test": "jest --coverage" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "fs-extra": "^8.1.0", "inquirer": "^7.3.3", diff --git a/packages/amplify-category-interactions/CHANGELOG.md b/packages/amplify-category-interactions/CHANGELOG.md index b3d3243f47d..ac1036c89c2 100644 --- a/packages/amplify-category-interactions/CHANGELOG.md +++ b/packages/amplify-category-interactions/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.6.6-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-interactions@2.6.5...amplify-category-interactions@2.6.6-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-category-interactions + + + + + ## [2.6.5](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-interactions@2.6.4...amplify-category-interactions@2.6.5) (2021-09-18) **Note:** Version bump only for package amplify-category-interactions diff --git a/packages/amplify-category-interactions/package.json b/packages/amplify-category-interactions/package.json index bebbd9e2ddd..40097ed83bc 100644 --- a/packages/amplify-category-interactions/package.json +++ b/packages/amplify-category-interactions/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-interactions", - "version": "2.6.5", + "version": "2.6.6-graphql-vnext-dev-preview.0", "description": "amplify-cli interactions plugin", "repository": { "type": "git", @@ -15,7 +15,7 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "fs-extra": "^8.1.0", "fuzzy": "^0.1.3", "inquirer": "^7.3.3", diff --git a/packages/amplify-category-predictions/CHANGELOG.md b/packages/amplify-category-predictions/CHANGELOG.md index ef8ac542b7f..b2dc8ed0bf9 100644 --- a/packages/amplify-category-predictions/CHANGELOG.md +++ b/packages/amplify-category-predictions/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.9.13-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-predictions@2.9.12...amplify-category-predictions@2.9.13-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-category-predictions + + + + + ## [2.9.12](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-predictions@2.9.11...amplify-category-predictions@2.9.12) (2021-09-18) **Note:** Version bump only for package amplify-category-predictions diff --git a/packages/amplify-category-predictions/package.json b/packages/amplify-category-predictions/package.json index f2b4ca21618..65201d702fa 100644 --- a/packages/amplify-category-predictions/package.json +++ b/packages/amplify-category-predictions/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-predictions", - "version": "2.9.12", + "version": "2.9.13-graphql-vnext-dev-preview.0", "description": "amplify-cli predictions plugin", "repository": { "type": "git", @@ -15,7 +15,7 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "aws-sdk": "^2.963.0", "chalk": "^4.1.1", "fs-extra": "^8.1.0", diff --git a/packages/amplify-category-storage/CHANGELOG.md b/packages/amplify-category-storage/CHANGELOG.md index 9d5ca73a2b5..3d33b1bbb0a 100644 --- a/packages/amplify-category-storage/CHANGELOG.md +++ b/packages/amplify-category-storage/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.12.10-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-storage@2.12.9...amplify-category-storage@2.12.10-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-category-storage + + + + + ## [2.12.9](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-storage@2.12.8...amplify-category-storage@2.12.9) (2021-09-18) **Note:** Version bump only for package amplify-category-storage diff --git a/packages/amplify-category-storage/package.json b/packages/amplify-category-storage/package.json index bd53c0c6e62..2e2af7185c9 100644 --- a/packages/amplify-category-storage/package.json +++ b/packages/amplify-category-storage/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-storage", - "version": "2.12.9", + "version": "2.12.10-graphql-vnext-dev-preview.0", "description": "amplify-cli storage plugin", "repository": { "type": "git", @@ -22,9 +22,9 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", - "amplify-prompts": "1.1.2", - "amplify-util-import": "1.5.12", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", + "amplify-prompts": "1.2.0-graphql-vnext-dev-preview.0", + "amplify-util-import": "1.5.13-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "cloudform-types": "^4.2.0", "enquirer": "^2.3.6", diff --git a/packages/amplify-category-xr/CHANGELOG.md b/packages/amplify-category-xr/CHANGELOG.md index 8210532238d..32bb5165420 100644 --- a/packages/amplify-category-xr/CHANGELOG.md +++ b/packages/amplify-category-xr/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.8.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-xr@2.8.21...amplify-category-xr@2.8.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-category-xr + + + + + ## [2.8.21](https://github.com/aws-amplify/amplify-cli/compare/amplify-category-xr@2.8.20...amplify-category-xr@2.8.21) (2021-09-18) **Note:** Version bump only for package amplify-category-xr diff --git a/packages/amplify-category-xr/package.json b/packages/amplify-category-xr/package.json index c2c3d13f25d..ac5c4266ead 100644 --- a/packages/amplify-category-xr/package.json +++ b/packages/amplify-category-xr/package.json @@ -1,6 +1,6 @@ { "name": "amplify-category-xr", - "version": "2.8.21", + "version": "2.8.22-graphql-vnext-dev-preview.0", "description": "amplify-cli xr plugin", "repository": { "type": "git", @@ -19,7 +19,7 @@ "test-watch": "jest --watch" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "fs-extra": "^8.1.0", "inquirer": "^7.3.3" diff --git a/packages/amplify-cli-core/CHANGELOG.md b/packages/amplify-cli-core/CHANGELOG.md index ebedb48de80..62f2ef6ac4e 100644 --- a/packages/amplify-cli-core/CHANGELOG.md +++ b/packages/amplify-cli-core/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.30.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-cli-core@1.29.0...amplify-cli-core@1.30.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* [#8223](https://github.com/aws-amplify/amplify-cli/issues/8223), conversion to typescript ([#8245](https://github.com/aws-amplify/amplify-cli/issues/8245)) ([096e6ca](https://github.com/aws-amplify/amplify-cli/commit/096e6ca19b94aa40ef249ea98d008380395afa16)) +* **amplify-cli-core:** add service mapping FFs ([#7024](https://github.com/aws-amplify/amplify-cli/issues/7024)) ([36fe24d](https://github.com/aws-amplify/amplify-cli/commit/36fe24db9f37a8a12d50f1e20ea44562eb44d04a)) +* toggle transformer v2 feature flag ([237c42c](https://github.com/aws-amplify/amplify-cli/commit/237c42cfc45fa24077fd29b682904efda83992fa)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* **amplify-provider-awscloudformation:** match env directive field for sandbox mode ([#3](https://github.com/aws-amplify/amplify-cli/issues/3)) ([3158549](https://github.com/aws-amplify/amplify-cli/commit/31585492fd4c358903d68d9640e9ac53e9b32eef)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + # [1.29.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-cli-core@1.28.0...amplify-cli-core@1.29.0) (2021-09-18) diff --git a/packages/amplify-cli-core/package.json b/packages/amplify-cli-core/package.json index 651efff8a80..e8073cacd65 100644 --- a/packages/amplify-cli-core/package.json +++ b/packages/amplify-cli-core/package.json @@ -1,6 +1,6 @@ { "name": "amplify-cli-core", - "version": "1.29.0", + "version": "1.30.0-graphql-vnext-dev-preview.0", "description": "Amplify CLI Core", "repository": { "type": "git", @@ -26,7 +26,7 @@ "dependencies": { "ajv": "^6.12.3", "amplify-cli-logger": "1.1.0", - "amplify-prompts": "1.1.2", + "amplify-prompts": "1.2.0-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "ci-info": "^2.0.0", "cloudform-types": "^4.2.0", diff --git a/packages/amplify-cli-core/src/feature-flags/featureFlags.ts b/packages/amplify-cli-core/src/feature-flags/featureFlags.ts index 88194256519..057869a5657 100644 --- a/packages/amplify-cli-core/src/feature-flags/featureFlags.ts +++ b/packages/amplify-cli-core/src/feature-flags/featureFlags.ts @@ -531,7 +531,7 @@ export class FeatureFlags { name: 'useExperimentalPipelinedTransformer', type: 'boolean', defaultValueForExistingProjects: false, - defaultValueForNewProjects: false, + defaultValueForNewProjects: true, }, { name: 'enableIterativeGSIUpdates', diff --git a/packages/amplify-cli-core/src/index.ts b/packages/amplify-cli-core/src/index.ts index c4ad0a04041..3051364baa7 100644 --- a/packages/amplify-cli-core/src/index.ts +++ b/packages/amplify-cli-core/src/index.ts @@ -251,7 +251,6 @@ interface AmplifyToolkit { category?: string, resourceName?: string, filteredResources?: { category: string; resourceName: string }[], - rebuild?: boolean, ) => $TSAny; storeCurrentCloudBackend: () => $TSAny; readJsonFile: (fileName: string) => $TSAny; diff --git a/packages/amplify-cli/CHANGELOG.md b/packages/amplify-cli/CHANGELOG.md index 40dda810eb8..acb3019e5ff 100644 --- a/packages/amplify-cli/CHANGELOG.md +++ b/packages/amplify-cli/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [6.1.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/cli@6.0.1...@aws-amplify/cli@6.1.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **amplify-category-auth:** update front end config on pull ([#8173](https://github.com/aws-amplify/amplify-cli/issues/8173)) ([da2b008](https://github.com/aws-amplify/amplify-cli/commit/da2b0083add2f5b10520efade8628080a34c8791)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* **amplify-provider-awscloudformation:** match env directive field for sandbox mode ([#3](https://github.com/aws-amplify/amplify-cli/issues/3)) ([3158549](https://github.com/aws-amplify/amplify-cli/commit/31585492fd4c358903d68d9640e9ac53e9b32eef)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [6.0.1](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/cli@6.0.0...@aws-amplify/cli@6.0.1) (2021-09-20) **Note:** Version bump only for package @aws-amplify/cli diff --git a/packages/amplify-cli/package.json b/packages/amplify-cli/package.json index c892a0c9b4e..03f3b61ef1b 100644 --- a/packages/amplify-cli/package.json +++ b/packages/amplify-cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/cli", - "version": "6.0.1", + "version": "6.1.0-graphql-vnext-dev-preview.0", "description": "Amplify CLI", "repository": { "type": "git", @@ -34,41 +34,41 @@ }, "dependencies": { "@aws-cdk/cloudformation-diff": "~1.124.0", - "amplify-app": "3.0.12", - "amplify-category-analytics": "2.21.21", - "amplify-category-api": "2.31.23", - "amplify-category-auth": "2.38.1", - "amplify-category-function": "2.34.7", - "amplify-category-geo": "1.0.0", - "amplify-category-hosting": "2.7.21", - "amplify-category-interactions": "2.6.5", + "amplify-app": "3.0.13-graphql-vnext-dev-preview.0", + "amplify-category-analytics": "2.21.22-graphql-vnext-dev-preview.0", + "amplify-category-api": "2.32.0-graphql-vnext-dev-preview.0", + "amplify-category-auth": "2.38.2-graphql-vnext-dev-preview.0", + "amplify-category-function": "2.35.0-graphql-vnext-dev-preview.0", + "amplify-category-geo": "1.0.1-graphql-vnext-dev-preview.0", + "amplify-category-hosting": "2.7.22-graphql-vnext-dev-preview.0", + "amplify-category-interactions": "2.6.6-graphql-vnext-dev-preview.0", "amplify-category-notifications": "2.19.4", - "amplify-category-predictions": "2.9.12", - "amplify-category-storage": "2.12.9", - "amplify-category-xr": "2.8.21", - "amplify-cli-core": "1.29.0", + "amplify-category-predictions": "2.9.13-graphql-vnext-dev-preview.0", + "amplify-category-storage": "2.12.10-graphql-vnext-dev-preview.0", + "amplify-category-xr": "2.8.22-graphql-vnext-dev-preview.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-cli-logger": "1.1.0", "amplify-codegen": "^2.23.1", - "amplify-console-hosting": "1.9.12", - "amplify-container-hosting": "1.3.23", + "amplify-console-hosting": "1.9.13-graphql-vnext-dev-preview.0", + "amplify-container-hosting": "1.3.24-graphql-vnext-dev-preview.0", "amplify-dotnet-function-runtime-provider": "1.6.3", - "amplify-dotnet-function-template-provider": "1.5.21", + "amplify-dotnet-function-template-provider": "1.5.22-graphql-vnext-dev-preview.0", "amplify-frontend-android": "2.15.4", "amplify-frontend-flutter": "0.4.4", - "amplify-frontend-ios": "2.20.14", - "amplify-frontend-javascript": "2.24.1", - "amplify-go-function-runtime-provider": "1.9.4", + "amplify-frontend-ios": "2.20.15-graphql-vnext-dev-preview.0", + "amplify-frontend-javascript": "2.24.2-graphql-vnext-dev-preview.0", + "amplify-go-function-runtime-provider": "1.9.5-graphql-vnext-dev-preview.0", "amplify-go-function-template-provider": "1.3.10", - "amplify-java-function-runtime-provider": "1.8.14", + "amplify-java-function-runtime-provider": "1.8.15-graphql-vnext-dev-preview.0", "amplify-java-function-template-provider": "1.5.9", - "amplify-nodejs-function-runtime-provider": "1.6.11", - "amplify-nodejs-function-template-provider": "1.6.21", - "amplify-prompts": "1.1.2", - "amplify-provider-awscloudformation": "4.60.1", - "amplify-python-function-runtime-provider": "1.9.11", + "amplify-nodejs-function-runtime-provider": "1.6.12-graphql-vnext-dev-preview.0", + "amplify-nodejs-function-template-provider": "1.6.22-graphql-vnext-dev-preview.0", + "amplify-prompts": "1.2.0-graphql-vnext-dev-preview.0", + "amplify-provider-awscloudformation": "4.61.0-graphql-vnext-dev-preview.0", + "amplify-python-function-runtime-provider": "1.9.12-graphql-vnext-dev-preview.0", "amplify-python-function-template-provider": "1.3.12", - "amplify-util-import": "1.5.12", - "amplify-util-mock": "3.34.5", + "amplify-util-import": "1.5.13-graphql-vnext-dev-preview.0", + "amplify-util-mock": "3.34.6-graphql-vnext-dev-preview.0", "aws-sdk": "^2.963.0", "chalk": "^4.1.1", "ci-info": "^2.0.0", @@ -84,7 +84,7 @@ "fs-extra": "^8.1.0", "glob": "^7.1.6", "global-prefix": "^3.0.0", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "gunzip-maybe": "^1.4.2", "hidefile": "^3.0.0", "ini": "^1.3.5", diff --git a/packages/amplify-cli/src/__tests__/commands/status.test.ts b/packages/amplify-cli/src/__tests__/commands/status.test.ts index d22a798a354..30f9dc59605 100644 --- a/packages/amplify-cli/src/__tests__/commands/status.test.ts +++ b/packages/amplify-cli/src/__tests__/commands/status.test.ts @@ -1,5 +1,3 @@ -import { UnknownArgumentError } from 'amplify-cli-core'; - describe('amplify status: ', () => { const { run } = require('../../commands/status'); const runStatusCmd = run; @@ -11,17 +9,10 @@ describe('amplify status: ', () => { }); it('status run method should call context.amplify.showStatusTable', async () => { - const cliInput = { - command: 'status', - subCommands: [], - options: { - verbose: true, - }, - }; - const mockContextNoCLArgs = { amplify: { showStatusTable: jest.fn(), + showGlobalSandboxModeWarning: jest.fn(), showHelpfulProviderLinks: jest.fn(), getCategoryPluginInfo: jest.fn().mockReturnValue({ packageLocation: mockPath }), }, @@ -43,6 +34,7 @@ describe('amplify status: ', () => { const mockContextWithVerboseOptionAndCLArgs = { amplify: { showStatusTable: jest.fn(), + showGlobalSandboxModeWarning: jest.fn(), showHelpfulProviderLinks: jest.fn(), getCategoryPluginInfo: jest.fn().mockReturnValue({ packageLocation: statusPluginInfo }), }, @@ -61,6 +53,7 @@ describe('amplify status: ', () => { const mockContextWithVerboseOptionWithCategoriesAndCLArgs = { amplify: { showStatusTable: jest.fn(), + showGlobalSandboxModeWarning: jest.fn(), showHelpfulProviderLinks: jest.fn(), getCategoryPluginInfo: jest.fn().mockReturnValue({ packageLocation: statusPluginInfo }), }, @@ -82,6 +75,7 @@ describe('amplify status: ', () => { const mockContextWithHelpSubcommandAndCLArgs = { amplify: { showStatusTable: jest.fn(), + showGlobalSandboxModeWarning: jest.fn(), showHelpfulProviderLinks: jest.fn(), getCategoryPluginInfo: jest.fn().mockReturnValue({ packageLocation: statusPluginInfo }), }, @@ -94,5 +88,4 @@ describe('amplify status: ', () => { //TBD: to move ViewResourceTableParams into a separate file for mocking instance functions. expect(mockContextWithHelpSubcommandAndCLArgs.amplify.showStatusTable.mock.calls.length).toBe(0); }); - }); diff --git a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta-2.json b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta-2.json new file mode 100644 index 00000000000..9bd3f8293be --- /dev/null +++ b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta-2.json @@ -0,0 +1,25 @@ +{ + "api": { + "ampapp": { + "service": "AppSync", + "providerPlugin": "awscloudformation", + "output": { + "authConfig": { + "defaultAuthentication": { + "authenticationType": "AWS_IAM" + }, + "additionalAuthenticationProviders": [ + { + "authenticationType": "API_KEY", + "apiKeyConfig": { + "apiKeyExpirationDays": 2, + "apiKeyExpirationDate": "2021-08-20T20:38:07.585Z", + "description": "" + } + } + ] + } + } + } + } +} diff --git a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta-3.json b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta-3.json new file mode 100644 index 00000000000..7aa92695860 --- /dev/null +++ b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta-3.json @@ -0,0 +1,16 @@ +{ + "api": { + "ampapp": { + "service": "AppSync", + "providerPlugin": "awscloudformation", + "output": { + "authConfig": { + "defaultAuthentication": { + "authenticationType": "AWS_IAM" + }, + "additionalAuthenticationProviders": [] + } + } + } + } +} diff --git a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta.json b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta.json new file mode 100644 index 00000000000..cb9a35d4025 --- /dev/null +++ b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/testData/mockLocalCloud/amplify-meta.json @@ -0,0 +1,28 @@ +{ + "api": { + "ampapp": { + "service": "AppSync", + "providerPlugin": "awscloudformation", + "output": { + "authConfig": { + "defaultAuthentication": { + "authenticationType": "AWS_IAM" + }, + "additionalAuthenticationProviders": [ + { + "authenticationType": "API_KEY", + "apiKeyConfig": { + "apiKeyExpirationDays": 2, + "apiKeyExpirationDate": "2021-08-20T20:38:07.585Z", + "description": "" + } + } + ] + }, + "globalSandboxModeConfig": { + "env": "dev" + } + } + } + } +} diff --git a/packages/amplify-cli/src/commands/status.ts b/packages/amplify-cli/src/commands/status.ts index 3e11576346a..b9dad7bf397 100644 --- a/packages/amplify-cli/src/commands/status.ts +++ b/packages/amplify-cli/src/commands/status.ts @@ -1,26 +1,27 @@ -import { ViewResourceTableParams, CLIParams, $TSContext } from "amplify-cli-core"; +import { ViewResourceTableParams, CLIParams, $TSContext } from 'amplify-cli-core'; +export const run = async (context: $TSContext) => { + const cliParams: CLIParams = { + cliCommand: context?.input?.command, + cliSubcommands: context?.input?.subCommands, + cliOptions: context?.input?.options, + }; -export const run = async (context : $TSContext) => { - const cliParams:CLIParams = { cliCommand : context?.input?.command, - cliSubcommands: context?.input?.subCommands, - cliOptions : context?.input?.options } - - const view = new ViewResourceTableParams( cliParams ); - if ( context?.input?.subCommands?.includes("help")){ - context.print.info( view.getStyledHelp() ); + const view = new ViewResourceTableParams(cliParams); + if (context?.input?.subCommands?.includes('help')) { + context.print.info(view.getStyledHelp()); } else { try { - await context.amplify.showStatusTable( view ); + await context.amplify.showStatusTable(view); await context.amplify.showHelpfulProviderLinks(context); await showAmplifyConsoleHostingStatus(context); - } catch ( e ){ + } catch (e) { view.logErrorException(e, context); } } }; -async function showAmplifyConsoleHostingStatus( context) { +async function showAmplifyConsoleHostingStatus(context) { const pluginInfo = context.amplify.getCategoryPluginInfo(context, 'hosting', 'amplifyhosting'); if (pluginInfo && pluginInfo.packageLocation) { const { status } = await import(pluginInfo.packageLocation); diff --git a/packages/amplify-cli/src/domain/amplify-toolkit.ts b/packages/amplify-cli/src/domain/amplify-toolkit.ts index 55f1b41b753..046f9fbbb3f 100644 --- a/packages/amplify-cli/src/domain/amplify-toolkit.ts +++ b/packages/amplify-cli/src/domain/amplify-toolkit.ts @@ -261,8 +261,7 @@ export class AmplifyToolkit { } get showStatusTable(): any { - this._showStatusTable = - this._showStatusTable || require(path.join(this._amplifyHelpersDirPath, 'resource-status')).showStatusTable; + this._showStatusTable = this._showStatusTable || require(path.join(this._amplifyHelpersDirPath, 'resource-status')).showStatusTable; return this._showStatusTable; } diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts b/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts index 3b86fc0c5e2..30c509aeb86 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts @@ -11,7 +11,6 @@ export async function pushResources( category?: string, resourceName?: string, filteredResources?: { category: string; resourceName: string }[], - rebuild: boolean = false, ) { if (context.parameters.options['iterative-rollback']) { // validate --iterative-rollback with --force @@ -50,21 +49,16 @@ export async function pushResources( } } - let hasChanges = false; - if (!rebuild) { - // status table does not have a way to show resource in "rebuild" state so skipping it to avoid confusion - hasChanges = await showResourceTable(category, resourceName, filteredResources); - } + const hasChanges = await showResourceTable(category, resourceName, filteredResources); // no changes detected - if (!hasChanges && !context.exeInfo.forcePush && !rebuild) { + if (!hasChanges && !context.exeInfo.forcePush) { context.print.info('\nNo changes detected'); return context; } - // rebuild has an upstream confirmation prompt so no need to prompt again here - let continueToPush = (context.exeInfo && context.exeInfo.inputParams && context.exeInfo.inputParams.yes) || rebuild; + let continueToPush = context.exeInfo && context.exeInfo.inputParams && context.exeInfo.inputParams.yes; if (!continueToPush) { if (context.exeInfo.iterativeRollback) { @@ -74,11 +68,27 @@ export async function pushResources( } if (continueToPush) { - // Get current-cloud-backend's amplify-meta - const currentAmplifyMeta = stateManager.getCurrentMeta(); - - await providersPush(context, rebuild, category, resourceName, filteredResources); - await onCategoryOutputsChange(context, currentAmplifyMeta); + let retryPush; + do { + retryPush = false; + try { + // Get current-cloud-backend's amplify-meta + const currentAmplifyMeta = stateManager.getCurrentMeta(); + + await providersPush(context, category, resourceName, filteredResources); + await onCategoryOutputsChange(context, currentAmplifyMeta); + } catch (err) { + if (await isValidGraphQLAuthError(err.message)) { + retryPush = await handleValidGraphQLAuthError(context, err.message); + } + + if (!retryPush) { + // Handle the errors and print them nicely for the user. + context.print.error(`\n${err.message}`); + throw err; + } + } + } while (retryPush); } else { // there's currently no other mechanism to stop the execution of the postPush workflow in this case, so exiting here exitOnNextTick(1); @@ -87,13 +97,39 @@ export async function pushResources( return continueToPush; } -async function providersPush( - context: $TSContext, - rebuild: boolean = false, - category?: string, - resourceName?: string, - filteredResources?: { category: string; resourceName: string }[], -) { +async function isValidGraphQLAuthError(message: string) { + if (message === `@auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.` + || message === `@auth directive with 'userPools' provider found, but the project has no Cognito User Pools authentication provider configured.` + || message === `@auth directive with 'oidc' provider found, but the project has no OPENID_CONNECT authentication provider configured.` + || message === `@auth directive with 'apiKey' provider found, but the project has no API Key authentication provider configured.` + || message === `@auth directive with 'function' provider found, but the project has no Lambda authentication provider configured.`) { + return true; + } +} + +async function handleValidGraphQLAuthError(context: $TSContext, message: string) { + if (message === `@auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.`) { + await addGraphQLAuthRequirement(context, 'AWS_IAM'); + return true; + } else if (!context?.parameters?.options?.yes) { + if (message === `@auth directive with 'userPools' provider found, but the project has no Cognito User Pools authentication provider configured.`) { + await addGraphQLAuthRequirement(context, 'AMAZON_COGNITO_USER_POOLS'); + return true; + } else if (message === `@auth directive with 'oidc' provider found, but the project has no OPENID_CONNECT authentication provider configured.`) { + await addGraphQLAuthRequirement(context, 'OPENID_CONNECT') + return true; + } else if (message === `@auth directive with 'apiKey' provider found, but the project has no API Key authentication provider configured.`) { + await addGraphQLAuthRequirement(context, 'AWS_KEY'); + return true; + } else if (message === `@auth directive with 'function' provider found, but the project has no Lambda authentication provider configured.`) { + await addGraphQLAuthRequirement(context, 'AWS_LAMBDA'); + return true; + } + } + return false; +} + +async function providersPush(context: $TSContext, category, resourceName, filteredResources) { const { providers } = getProjectConfig(); const providerPlugins = getProviderPlugins(context); const providerPromises: (() => Promise<$TSAny>)[] = []; @@ -101,7 +137,7 @@ async function providersPush( for (const provider of providers) { const providerModule = require(providerPlugins[provider]); const resourceDefiniton = await context.amplify.getResourceStatus(category, resourceName, provider, filteredResources); - providerPromises.push(providerModule.pushResources(context, resourceDefiniton, rebuild)); + providerPromises.push(providerModule.pushResources(context, resourceDefiniton)); } await Promise.all(providerPromises); @@ -119,3 +155,14 @@ export async function storeCurrentCloudBackend(context: $TSContext) { await Promise.all(providerPromises); } + +async function addGraphQLAuthRequirement(context, authType) { + return await context.amplify.invokePluginMethod(context, 'api', undefined, 'addGraphQLAuthorizationMode', [ + context, + { + authType: authType, + printLeadText: true, + authSettings: undefined, + }, + ]); +} \ No newline at end of file diff --git a/packages/amplify-codegen-appsync-model-plugin/src/scalars/supported-directives.ts b/packages/amplify-codegen-appsync-model-plugin/src/scalars/supported-directives.ts index 262b075641c..0fde82582cd 100644 --- a/packages/amplify-codegen-appsync-model-plugin/src/scalars/supported-directives.ts +++ b/packages/amplify-codegen-appsync-model-plugin/src/scalars/supported-directives.ts @@ -41,7 +41,7 @@ export const directives = /* GraphQL */ ` directive @auth(rules: [AuthRule!]!) on OBJECT | FIELD_DEFINITION input AuthRule { - # Specifies the auth rule's strategy. Allowed values are 'owner', 'groups', 'public', 'private'. + # Specifies the auth rule's strategy. Allowed values are 'owner', 'groups', 'public', 'private', 'custom'. allow: AuthStrategy! # Legacy name for identityClaim @@ -90,6 +90,7 @@ export const directives = /* GraphQL */ ` groups private public + custom } enum AuthProvider { @@ -97,6 +98,7 @@ export const directives = /* GraphQL */ ` iam oidc userPools + function } enum ModelOperation { diff --git a/packages/amplify-codegen-appsync-model-plugin/src/utils/process-auth.ts b/packages/amplify-codegen-appsync-model-plugin/src/utils/process-auth.ts index 08e09ace75b..0b15cc76c6d 100644 --- a/packages/amplify-codegen-appsync-model-plugin/src/utils/process-auth.ts +++ b/packages/amplify-codegen-appsync-model-plugin/src/utils/process-auth.ts @@ -4,12 +4,14 @@ export enum AuthProvider { iam = 'iam', oidc = 'oidc', userPools = 'userPools', + function = 'function', } export enum AuthStrategy { owner = 'owner', groups = 'groups', private = 'private', public = 'public', + custom = 'custom', } export enum AuthModelOperation { diff --git a/packages/amplify-codegen-appsync-model-plugin/src/visitors/appsync-java-visitor.ts b/packages/amplify-codegen-appsync-model-plugin/src/visitors/appsync-java-visitor.ts index 57309ef5766..7d8f1d2f912 100644 --- a/packages/amplify-codegen-appsync-model-plugin/src/visitors/appsync-java-visitor.ts +++ b/packages/amplify-codegen-appsync-model-plugin/src/visitors/appsync-java-visitor.ts @@ -800,6 +800,9 @@ export class AppSyncModelJavaVisitor< case AuthStrategy.public: authRule.push('allow = AuthStrategy.PUBLIC'); break; + case AuthStrategy.custom: + authRule.push('allow = AuthStrategy.CUSTOM'); + break; case AuthStrategy.groups: authRule.push('allow = AuthStrategy.GROUPS'); authRule.push(`groupClaim = "${rule.groupClaim}"`); diff --git a/packages/amplify-console-hosting/CHANGELOG.md b/packages/amplify-console-hosting/CHANGELOG.md index e7b8e3439aa..2790f69d52f 100644 --- a/packages/amplify-console-hosting/CHANGELOG.md +++ b/packages/amplify-console-hosting/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.9.13-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-console-hosting@1.9.12...amplify-console-hosting@1.9.13-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-console-hosting + + + + + ## [1.9.12](https://github.com/aws-amplify/amplify-cli/compare/amplify-console-hosting@1.9.11...amplify-console-hosting@1.9.12) (2021-09-18) **Note:** Version bump only for package amplify-console-hosting diff --git a/packages/amplify-console-hosting/package.json b/packages/amplify-console-hosting/package.json index 12148428543..f96489fa930 100644 --- a/packages/amplify-console-hosting/package.json +++ b/packages/amplify-console-hosting/package.json @@ -1,12 +1,12 @@ { "name": "amplify-console-hosting", - "version": "1.9.12", + "version": "1.9.13-graphql-vnext-dev-preview.0", "description": "cli plugin for AWS Amplify Console hosting", "main": "index.js", "author": "Amazon Web Services", "license": "Apache-2.0", "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "archiver": "^5.3.0", "aws-sdk": "^2.963.0", "chalk": "^4.1.1", diff --git a/packages/amplify-console-integration-tests/CHANGELOG.md b/packages/amplify-console-integration-tests/CHANGELOG.md index 7bccafba147..d9650e9032e 100644 --- a/packages/amplify-console-integration-tests/CHANGELOG.md +++ b/packages/amplify-console-integration-tests/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.8.10-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-console-integration-tests/compare/amplify-console-integration-tests@1.8.9...amplify-console-integration-tests@1.8.10-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-console-integration-tests + + + + + ## [1.8.9](https://github.com/aws-amplify/amplify-console-integration-tests/compare/amplify-console-integration-tests@1.8.8...amplify-console-integration-tests@1.8.9) (2021-09-18) **Note:** Version bump only for package amplify-console-integration-tests diff --git a/packages/amplify-console-integration-tests/package.json b/packages/amplify-console-integration-tests/package.json index cf1d3a2918e..e52e2e71be1 100644 --- a/packages/amplify-console-integration-tests/package.json +++ b/packages/amplify-console-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "amplify-console-integration-tests", - "version": "1.8.9", + "version": "1.8.10-graphql-vnext-dev-preview.0", "description": "", "repository": { "type": "git", @@ -22,7 +22,7 @@ }, "dependencies": { "@types/ini": "^1.3.30", - "amplify-e2e-core": "1.26.1", + "amplify-e2e-core": "1.27.0-graphql-vnext-dev-preview.0", "aws-sdk": "^2.963.0", "dotenv": "^8.2.0", "esm": "^3.2.25", diff --git a/packages/amplify-container-hosting/CHANGELOG.md b/packages/amplify-container-hosting/CHANGELOG.md index ed8310816d1..c72fa156621 100644 --- a/packages/amplify-container-hosting/CHANGELOG.md +++ b/packages/amplify-container-hosting/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.3.24-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-container-hosting@1.3.23...amplify-container-hosting@1.3.24-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-container-hosting + + + + + ## [1.3.23](https://github.com/aws-amplify/amplify-cli/compare/amplify-container-hosting@1.3.22...amplify-container-hosting@1.3.23) (2021-09-18) **Note:** Version bump only for package amplify-container-hosting diff --git a/packages/amplify-container-hosting/package.json b/packages/amplify-container-hosting/package.json index 544c959bcc7..e3fc19de22a 100644 --- a/packages/amplify-container-hosting/package.json +++ b/packages/amplify-container-hosting/package.json @@ -1,6 +1,6 @@ { "name": "amplify-container-hosting", - "version": "1.3.23", + "version": "1.3.24-graphql-vnext-dev-preview.0", "description": "amplify-cli hosting plugin for containers", "repository": { "type": "git", @@ -18,7 +18,7 @@ "test": "jest --coverage --passWithNoTests" }, "dependencies": { - "amplify-category-api": "2.31.23", + "amplify-category-api": "2.32.0-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "fs-extra": "^8.1.0", "inquirer": "^7.3.3", diff --git a/packages/amplify-dotnet-function-template-provider/CHANGELOG.md b/packages/amplify-dotnet-function-template-provider/CHANGELOG.md index a465ead4ef7..4b8297bf576 100644 --- a/packages/amplify-dotnet-function-template-provider/CHANGELOG.md +++ b/packages/amplify-dotnet-function-template-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-dotnet-function-template-provider@1.5.21...amplify-dotnet-function-template-provider@1.5.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-dotnet-function-template-provider + + + + + ## [1.5.21](https://github.com/aws-amplify/amplify-cli/compare/amplify-dotnet-function-template-provider@1.5.20...amplify-dotnet-function-template-provider@1.5.21) (2021-09-18) **Note:** Version bump only for package amplify-dotnet-function-template-provider diff --git a/packages/amplify-dotnet-function-template-provider/package.json b/packages/amplify-dotnet-function-template-provider/package.json index 0b4551f1c9a..232a31baed8 100644 --- a/packages/amplify-dotnet-function-template-provider/package.json +++ b/packages/amplify-dotnet-function-template-provider/package.json @@ -1,6 +1,6 @@ { "name": "amplify-dotnet-function-template-provider", - "version": "1.5.21", + "version": "1.5.22-graphql-vnext-dev-preview.0", "description": ".NET Core templates supplied by the Amplify Team", "repository": { "type": "git", @@ -22,9 +22,9 @@ "clean": "rimraf lib tsconfig.tsbuildinfo" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", - "graphql-transformer-core": "6.29.7" + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/inquirer": "^6.5.0", diff --git a/packages/amplify-dynamodb-simulator/CHANGELOG.md b/packages/amplify-dynamodb-simulator/CHANGELOG.md index 8ea948e03e5..f02ccb1a152 100644 --- a/packages/amplify-dynamodb-simulator/CHANGELOG.md +++ b/packages/amplify-dynamodb-simulator/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.19.13-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-dynamodb-simulator@1.19.12...amplify-dynamodb-simulator@1.19.13-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-dynamodb-simulator + + + + + ## [1.19.12](https://github.com/aws-amplify/amplify-cli/compare/amplify-dynamodb-simulator@1.19.11...amplify-dynamodb-simulator@1.19.12) (2021-09-18) **Note:** Version bump only for package amplify-dynamodb-simulator diff --git a/packages/amplify-dynamodb-simulator/package.json b/packages/amplify-dynamodb-simulator/package.json index 401d0082075..3122dc14d87 100644 --- a/packages/amplify-dynamodb-simulator/package.json +++ b/packages/amplify-dynamodb-simulator/package.json @@ -1,6 +1,6 @@ { "name": "amplify-dynamodb-simulator", - "version": "1.19.12", + "version": "1.19.13-graphql-vnext-dev-preview.0", "description": "DynamoDB emulator nodejs wrapper", "repository": { "type": "git", @@ -20,7 +20,7 @@ "test": "jest" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "aws-sdk": "^2.963.0", "detect-port": "^1.3.0", "event-to-promise": "^0.8.0", diff --git a/packages/amplify-e2e-core/CHANGELOG.md b/packages/amplify-e2e-core/CHANGELOG.md index e42609100f1..152dac2238e 100644 --- a/packages/amplify-e2e-core/CHANGELOG.md +++ b/packages/amplify-e2e-core/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.27.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-e2e-core@1.26.1...amplify-e2e-core@1.27.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* **cli-api:** improve add and update api ([02af6b5](https://github.com/aws-amplify/amplify-cli/commit/02af6b582367b584ad755c889fb04722af355368)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [1.26.1](https://github.com/aws-amplify/amplify-cli/compare/amplify-e2e-core@1.26.0...amplify-e2e-core@1.26.1) (2021-09-18) **Note:** Version bump only for package amplify-e2e-core diff --git a/packages/amplify-e2e-core/package.json b/packages/amplify-e2e-core/package.json index b8a5fbc80d7..cdb548f0a4e 100644 --- a/packages/amplify-e2e-core/package.json +++ b/packages/amplify-e2e-core/package.json @@ -1,6 +1,6 @@ { "name": "amplify-e2e-core", - "version": "1.26.1", + "version": "1.27.0-graphql-vnext-dev-preview.0", "description": "", "repository": { "type": "git", @@ -22,12 +22,12 @@ "clean": "rimraf ./lib" }, "dependencies": { - "amplify-cli-core": "1.29.0", - "amplify-headless-interface": "1.10.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", + "amplify-headless-interface": "1.11.0-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "execa": "^5.1.1", "fs-extra": "^8.1.0", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "jest-environment-node": "^26.6.2", "lodash": "^4.17.21", "node-pty-prebuilt-multiarch": "^0.9.0", diff --git a/packages/amplify-e2e-core/src/categories/api.ts b/packages/amplify-e2e-core/src/categories/api.ts index 392ad64a427..833b698fcba 100644 --- a/packages/amplify-e2e-core/src/categories/api.ts +++ b/packages/amplify-e2e-core/src/categories/api.ts @@ -30,24 +30,19 @@ const defaultOptions: AddApiOptions = { apiName: '\r', }; -export function addApiWithoutSchema(cwd: string, opts: Partial = {}) { +export function addApiWithoutSchema(cwd: string, opts: Partial = {}) { const options = _.assign(defaultOptions, opts); return new Promise((resolve, reject) => { spawn(getCLIPath(), ['add', 'api'], { cwd, stripColors: true }) .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendKeyUp(3) + .sendCarriageReturn() .wait('Provide API name:') .sendLine(options.apiName) - .wait(/.*Choose the default authorization type for the API.*/) - .sendCarriageReturn() - .wait(/.*Enter a description for the API key.*/) - .sendCarriageReturn() - .wait(/.*After how many days from now the API key should expire.*/) - .sendCarriageReturn() - .wait(/.*Do you want to configure advanced settings for the GraphQL API.*/) + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) .sendCarriageReturn() - .wait('Do you have an annotated GraphQL schema?') - .sendLine('n') .wait('Choose a schema template:') .sendCarriageReturn() .wait('Do you want to edit the schema now?') @@ -65,27 +60,58 @@ export function addApiWithoutSchema(cwd: string, opts: Partial = }); } -export function addApiWithSchema(cwd: string, schemaFile: string, opts: Partial = {}) { +export function addApiWithBlankSchema(cwd: string, opts: Partial = {}) { const options = _.assign(defaultOptions, opts); - const schemaPath = getSchemaPath(schemaFile); return new Promise((resolve, reject) => { spawn(getCLIPath(), ['add', 'api'], { cwd, stripColors: true }) .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendKeyUp(3) + .sendCarriageReturn() .wait('Provide API name:') .sendLine(options.apiName) - .wait(/.*Choose the default authorization type for the API.*/) + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) .sendCarriageReturn() - .wait(/.*Enter a description for the API key.*/) + .wait('Choose a schema template:') + .sendKeyDown(2) .sendCarriageReturn() - .wait(/.*After how many days from now the API key should expire.*/) - .sendLine(opts.apiKeyExpirationDays ? opts.apiKeyExpirationDays.toString() : '1') - .wait(/.*Do you want to configure advanced settings for the GraphQL API.*/) + .wait('Do you want to edit the schema now?') + .sendLine('n') + .wait( + '"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud', + ) + .sendEof() + .run((err: Error) => { + if (!err) { + resolve(); + } else { + reject(err); + } + }); + + }); +} + +export function addApiWithBlankSchemaAndConflictDetection(cwd: string) { + return new Promise((resolve, reject) => { + spawn(getCLIPath(), ['add', 'api'], { cwd, stripColors: true }) + .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() - .wait('Do you have an annotated GraphQL schema?') + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendKeyUp() + .sendCarriageReturn() + .wait(/.*Enable conflict detection.*/) .sendLine('y') - .wait('Provide your schema file path:') - .sendLine(schemaPath) + .wait(/.*Select the default resolution strategy.*/) + .sendCarriageReturn() + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendCarriageReturn() + .wait('Choose a schema template:') + .sendKeyDown(2) + .sendCarriageReturn() + .wait('Do you want to edit the schema now?') + .sendLine('n') .wait( '"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud', ) @@ -99,32 +125,65 @@ export function addApiWithSchema(cwd: string, schemaFile: string, opts: Partial< }); } -export function addApiWithSchemaAndConflictDetection(cwd: string, schemaFile: string) { - const schemaPath = getSchemaPath(schemaFile); +/** + * Note: Lambda Authorizer is enabled only for Transformer V2 + */ +export function addApiWithAllAuthModesV2(cwd: string, opts: Partial = {}) { + const options = _.assign(defaultOptions, opts); return new Promise((resolve, reject) => { spawn(getCLIPath(), ['add', 'api'], { cwd, stripColors: true }) .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendKeyUp(3) + .sendCarriageReturn() .wait('Provide API name:') + .sendLine(options.apiName) + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendKeyUp(2) .sendCarriageReturn() .wait(/.*Choose the default authorization type for the API.*/) .sendCarriageReturn() + // API Key .wait(/.*Enter a description for the API key.*/) - .sendCarriageReturn() + .sendLine('description') .wait(/.*After how many days from now the API key should expire.*/) - .sendCarriageReturn() - .wait(/.*Do you want to configure advanced settings for the GraphQL API.*/) - .sendLine(KEY_DOWN_ARROW) // Down + .sendLine('300') .wait(/.*Configure additional auth types.*/) - .sendLine('n') - .wait(/.*Enable conflict detection.*/) .sendLine('y') - .wait(/.*Select the default resolution strategy.*/) + .wait(/.*Choose the additional authorization types you want to configure for the API.*/) + .sendLine('a\r') // All items + // Cognito + .wait(/.*Do you want to use the default authentication and security configuration.*/) .sendCarriageReturn() - .wait(/.*Do you have an annotated GraphQL schema.*/) - .sendLine('y') - .wait('Provide your schema file path:') - .sendLine(schemaPath) + .wait('How do you want users to be able to sign in?') + .sendCarriageReturn() + .wait('Do you want to configure advanced settings?') + .sendCarriageReturn() + // OIDC + .wait(/.*Enter a name for the OpenID Connect provider:.*/) + .sendLine('myoidcprovider') + .wait(/.*Enter the OpenID Connect provider domain \(Issuer URL\).*/) + .sendLine('https://facebook.com/') + .wait(/.*Enter the Client Id from your OpenID Client Connect application.*/) + .sendLine('clientId') + .wait(/.*Enter the number of milliseconds a token is valid after being issued to a user.*/) + .sendLine('1000') + .wait(/.*Enter the number of milliseconds a token is valid after being authenticated.*/) + .sendLine('2000') + // Lambda + .wait(/.*Choose a Lambda source*/) + .sendCarriageReturn() + .wait(/.*How long should the authorization response be cached in seconds.*/) + .sendLine('600') + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendCarriageReturn() + // Schema selection + .wait('Choose a schema template:') + .sendKeyDown(2) + .sendCarriageReturn() + .wait('Do you want to edit the schema now?') + .sendLine('n') .wait( '"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud', ) @@ -152,7 +211,7 @@ export function updateApiWithMultiAuth(cwd: string, settings: any) { spawn(getCLIPath(settings.testingWithLatestCodebase), ['update', 'api'], { cwd, stripColors: true }) .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() - .wait('Select from the options below') + .wait(/.*Select a setting to edit.*/) .sendCarriageReturn() .wait(/.*Choose the default authorization type for the API.*/) .sendCarriageReturn() @@ -160,8 +219,6 @@ export function updateApiWithMultiAuth(cwd: string, settings: any) { .sendLine('description') .wait(/.*After how many days from now the API key should expire.*/) .sendLine('300') - .wait(/.*Do you want to configure advanced settings for the GraphQL API.*/) - .sendLine(KEY_DOWN_ARROW) // Down .wait(/.*Configure additional auth types.*/) .sendLine('y') .wait(/.*Choose the additional authorization types you want to configure for the API.*/) @@ -184,8 +241,6 @@ export function updateApiWithMultiAuth(cwd: string, settings: any) { .sendLine('1000') .wait(/.*Enter the number of milliseconds a token is valid after being authenticated.*/) .sendLine('2000') - .wait('Enable conflict detection?') - .sendLine('n') .wait(/.*Successfully updated resource.*/) .sendEof() .run((err: Error) => { @@ -198,14 +253,18 @@ export function updateApiWithMultiAuth(cwd: string, settings: any) { }); } -export function apiUpdateToggleDataStore(cwd: string, settings: any) { +export function apiEnableDataStore(cwd: string, settings: any) { return new Promise((resolve, reject) => { spawn(getCLIPath(settings.testingWithLatestCodebase), ['update', 'api'], { cwd, stripColors: true }) .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() - .wait('Select from the options below') - .send(KEY_DOWN_ARROW) - .sendLine(KEY_DOWN_ARROW) // select enable datastore for the api + .wait(/.*Select a setting to edit.*/) + .sendKeyDown() + .sendCarriageReturn() + .wait(/.*Select the default resolution strategy.*/) + .sendCarriageReturn() + .wait(/.*Do you want to override default per model settings?.*/) + .sendConfirmNo() .wait(/.*Successfully updated resource.*/) .sendEof() .run((err: Error) => { @@ -218,29 +277,62 @@ export function apiUpdateToggleDataStore(cwd: string, settings: any) { }); } -export function updateAPIWithResolutionStrategy(cwd: string, settings: any) { +export function apiDisableDataStore(cwd: string, settings: any) { return new Promise((resolve, reject) => { spawn(getCLIPath(settings.testingWithLatestCodebase), ['update', 'api'], { cwd, stripColors: true }) .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() - .wait('Select from the options below') + .wait(/.*Select a setting to edit.*/) + .sendKeyDown(2) // Disable conflict detection .sendCarriageReturn() - .wait(/.*Choose the default authorization type for the API.*/) + .wait(/.*Successfully updated resource.*/) + .sendEof() + .run((err: Error) => { + if (!err) { + resolve(); + } else { + reject(err); + } + }); + }); +} + +export function updateAPIWithResolutionStrategyWithoutModels(cwd: string, settings: any) { + return new Promise((resolve, reject) => { + spawn(getCLIPath(settings.testingWithLatestCodebase), ['update', 'api'], { cwd, stripColors: true }) + .wait('Please select from one of the below mentioned services:') .sendCarriageReturn() - .wait(/.*Enter a description for the API key.*/) + .wait(/.*Select a setting to edit.*/) + .sendKeyDown() .sendCarriageReturn() - .wait(/.*After how many days from now the API key should expire.*/) + .wait(/.*Select the default resolution strategy.*/) + .sendKeyDown() + .sendCarriageReturn() + .wait(/.*Successfully updated resource.*/) + .sendEof() + .run((err: Error) => { + if (!err) { + resolve(); + } else { + reject(err); + } + }); + }); +} + +export function updateAPIWithResolutionStrategyWithModels(cwd: string, settings: any) { + return new Promise((resolve, reject) => { + spawn(getCLIPath(settings.testingWithLatestCodebase), ['update', 'api'], { cwd, stripColors: true }) + .wait('Please select from one of the below mentioned services:') + .sendCarriageReturn() + .wait(/.*Select a setting to edit.*/) + .sendKeyDown() .sendCarriageReturn() - .wait(/.*Do you want to configure advanced settings for the GraphQL API.*/) - .sendLine(KEY_DOWN_ARROW) // Down - .wait(/.*Configure additional auth types.*/) - .sendLine('n') - .wait(/.*Enable conflict detection.*/) - .sendLine('y') .wait(/.*Select the default resolution strategy.*/) - .sendLine(KEY_DOWN_ARROW) // Down - .wait(/.*Do you want to override default per model settings.*/) - .sendLine('n') + .sendKeyDown() + .sendCarriageReturn() + .wait(/.*Do you want to override default per model settings?.*/) + .sendConfirmNo() .wait(/.*Successfully updated resource.*/) .sendEof() .run((err: Error) => { @@ -384,26 +476,24 @@ export function addApi(projectDir: string, settings?: any) { return new Promise((resolve, reject) => { let chain = spawn(getCLIPath(), ['add', 'api'], { cwd: projectDir, stripColors: true }) .wait('Please select from one of the below mentioned services:') - .sendCarriageReturn() - .wait('Provide API name:') .sendCarriageReturn(); if (settings && Object.keys(settings).length > 0) { const authTypesToAdd = Object.keys(settings); const defaultType = authTypesToAdd[0]; + chain + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendKeyUp(2) + .sendCarriageReturn(); + singleSelect(chain.wait('Choose the default authorization type for the API'), defaultType, authTypesToSelectFrom); setupAuthType(defaultType, chain, settings); if (authTypesToAdd.length > 1) { authTypesToAdd.shift(); - chain - .wait('Do you want to configure advanced settings for the GraphQL API') - .send(KEY_DOWN_ARROW) //yes - .sendCarriageReturn() - .wait('Configure additional auth types?') - .sendLine('y'); + chain.wait('Configure additional auth types?').sendLine('y'); authTypesToSelectFrom = authTypesToSelectFrom.filter(x => x !== defaultType); @@ -416,21 +506,14 @@ export function addApi(projectDir: string, settings?: any) { authTypesToAdd.forEach(authType => { setupAuthType(authType, chain, settings); }); - - chain.wait('Enable conflict detection?').sendCarriageReturn(); //No } else { - chain.wait('Do you want to configure advanced settings for the GraphQL API').sendCarriageReturn(); //No + chain.wait('Configure additional auth types?').sendLine('n'); } - } else { - chain.wait('Choose the default authorization type for the API').sendCarriageReturn(); - setupAPIKey(chain); - - chain.wait('Do you want to configure advanced settings for the GraphQL API').sendCarriageReturn(); //No } chain - .wait('Do you have an annotated GraphQL schema?') - .sendLine('n') + .wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/) + .sendCarriageReturn() .wait('Choose a schema template:') .sendCarriageReturn() .wait('Do you want to edit the schema now?') @@ -566,13 +649,3 @@ export function addRestContainerApi(projectDir: string) { }); }); } - -export function rebuildApi(projDir: string, apiName: string) { - return new Promise((resolve, reject) => { - spawn(getCLIPath(), ['rebuild', 'api'], { cwd: projDir, stripColors: true }) - .wait('Type the name of the API to confirm you want to continue') - .sendLine(apiName) - .wait('All resources are updated in the cloud') - .run(err => (err ? reject(err) : resolve())); - }); -} diff --git a/packages/amplify-e2e-core/src/init/amplifyPush.ts b/packages/amplify-e2e-core/src/init/amplifyPush.ts index bf359bb3750..ebfba2e7730 100644 --- a/packages/amplify-e2e-core/src/init/amplifyPush.ts +++ b/packages/amplify-e2e-core/src/init/amplifyPush.ts @@ -36,6 +36,39 @@ export function amplifyPush(cwd: string, testingWithLatestCodebase: boolean = fa }); } +export function amplifyPushGraphQlWithCognitoPrompt(cwd: string, testingWithLatestCodebase: boolean = false): Promise { + return new Promise((resolve, reject) => { + //Test detailed status + spawn(getCLIPath(testingWithLatestCodebase), ['status', '-v'], { cwd, stripColors: true, noOutputTimeout: pushTimeoutMS }) + .wait(/.*/) + .run((err: Error) => { + if (err) { + reject(err); + } + }); + //Test amplify push + spawn(getCLIPath(testingWithLatestCodebase), ['push'], { cwd, stripColors: true, noOutputTimeout: pushTimeoutMS }) + .wait('Are you sure you want to continue?') + .sendConfirmYes() + .wait(/.*Do you want to use the default authentication and security configuration.*/) + .sendCarriageReturn() + .wait(/.*How do you want users to be able to sign in.*/) + .sendCarriageReturn() + .wait(/.*Do you want to configure advanced settings.*/) + .sendCarriageReturn() + .wait('Do you want to generate code for your newly created GraphQL API') + .sendLine('n') + .wait(/.*/) + .run((err: Error) => { + if (!err) { + resolve(); + } else { + reject(err); + } + }); + }); +} + export function amplifyPushForce(cwd: string, testingWithLatestCodebase: boolean = false): Promise { return new Promise((resolve, reject) => { spawn(getCLIPath(testingWithLatestCodebase), ['push', '--force'], { cwd, stripColors: true, noOutputTimeout: pushTimeoutMS }) @@ -251,21 +284,6 @@ export function amplifyPushWithNoChanges(cwd: string, testingWithLatestCodebase: return new Promise((resolve, reject) => { spawn(getCLIPath(testingWithLatestCodebase), ['push'], { cwd, stripColors: true, noOutputTimeout: pushTimeoutMS }) .wait('No changes detected') - .run((err: Error) => err ? reject(err) : resolve()); - }); -} - -export function amplifyPushDestructiveApiUpdate(cwd: string, includeForce: boolean) { - return new Promise((resolve, reject) => { - const args = ['push', '--yes']; - if (includeForce) { - args.push('--force'); - } - const chain = spawn(getCLIPath(), args, { cwd, stripColors: true }); - if (includeForce) { - chain.wait('All resources are updated in the cloud').run(err => (err ? reject(err) : resolve())); - } else { - chain.wait('If this is intended, rerun the command with').run(err => (err ? resolve(err) : reject())); // in this case, we expect the CLI to error out - } + .run((err: Error) => (err ? reject(err) : resolve())); }); } diff --git a/packages/amplify-e2e-core/src/utils/sdk-calls.ts b/packages/amplify-e2e-core/src/utils/sdk-calls.ts index 3b4da4e0f8f..4c38212bab2 100644 --- a/packages/amplify-e2e-core/src/utils/sdk-calls.ts +++ b/packages/amplify-e2e-core/src/utils/sdk-calls.ts @@ -201,16 +201,6 @@ export const deleteTable = async (tableName: string, region: string) => { return await service.deleteTable({ TableName: tableName }).promise(); }; -export const putItemInTable = async (tableName: string, region: string, item: unknown) => { - const ddb = new DynamoDB.DocumentClient({ region }); - return await ddb.put({ TableName: tableName, Item: item }).promise(); -}; - -export const scanTable = async (tableName: string, region: string) => { - const ddb = new DynamoDB.DocumentClient({ region }); - return await ddb.scan({ TableName: tableName }).promise(); -}; - export const getAppSyncApi = async (appSyncApiId: string, region: string) => { const service = new AppSync({ region }); return await service.getGraphqlApi({ apiId: appSyncApiId }).promise(); @@ -352,15 +342,19 @@ export const getSSMParameters = async (region: string, appId: string, envName: s }; //Amazon location service calls export const getMap = async (mapName: string, region: string) => { - const service = new Location({region}); - return await service.describeMap({ - MapName: mapName - }).promise() -} + const service = new Location({ region }); + return await service + .describeMap({ + MapName: mapName, + }) + .promise(); +}; export const getPlaceIndex = async (placeIndexName: string, region: string) => { - const service = new Location({region}); - return await service.describePlaceIndex({ - IndexName: placeIndexName - }).promise() -} + const service = new Location({ region }); + return await service + .describePlaceIndex({ + IndexName: placeIndexName, + }) + .promise(); +}; diff --git a/packages/amplify-e2e-tests/CHANGELOG.md b/packages/amplify-e2e-tests/CHANGELOG.md index f4101ae3ae1..1dc34844a2c 100644 --- a/packages/amplify-e2e-tests/CHANGELOG.md +++ b/packages/amplify-e2e-tests/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.51.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-e2e-tests@2.50.0...amplify-e2e-tests@2.51.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* **cli-api:** improve add and update api ([02af6b5](https://github.com/aws-amplify/amplify-cli/commit/02af6b582367b584ad755c889fb04722af355368)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + # [2.50.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-e2e-tests@2.49.0...amplify-e2e-tests@2.50.0) (2021-09-18) diff --git a/packages/amplify-e2e-tests/package.json b/packages/amplify-e2e-tests/package.json index cd9917f05c9..2203610b20f 100644 --- a/packages/amplify-e2e-tests/package.json +++ b/packages/amplify-e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "amplify-e2e-tests", - "version": "2.50.0", + "version": "2.51.0-graphql-vnext-dev-preview.0", "description": "", "repository": { "type": "git", @@ -23,8 +23,8 @@ "clean-e2e-resources": "ts-node ./src/cleanup-e2e-resources.ts" }, "dependencies": { - "amplify-cli-core": "1.29.0", - "amplify-e2e-core": "1.26.1", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", + "amplify-e2e-core": "1.27.0-graphql-vnext-dev-preview.0", "aws-amplify": "^4.2.8", "aws-appsync": "^4.1.1", "aws-sdk": "^2.963.0", @@ -35,7 +35,7 @@ "fs-extra": "^8.1.0", "graphql-schema-diff": "^2.2.0", "graphql-tag": "^2.10.1", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "lodash": "^4.17.21", "node-fetch": "^2.6.1", "promise-sequential": "^1.1.1", diff --git a/packages/amplify-e2e-tests/schemas/cognito_simple_model.graphql b/packages/amplify-e2e-tests/schemas/cognito_simple_model.graphql new file mode 100644 index 00000000000..d7b78a08560 --- /dev/null +++ b/packages/amplify-e2e-tests/schemas/cognito_simple_model.graphql @@ -0,0 +1,4 @@ +type Todo @model @auth(rules: [ { allow: owner, provider: userPools }]) { + id: ID! + content: String +} diff --git a/packages/amplify-e2e-tests/schemas/model_with_sandbox_mode.graphql b/packages/amplify-e2e-tests/schemas/model_with_sandbox_mode.graphql new file mode 100644 index 00000000000..22c833e9c67 --- /dev/null +++ b/packages/amplify-e2e-tests/schemas/model_with_sandbox_mode.graphql @@ -0,0 +1,6 @@ +type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: "dev") + +type Todo @model { + id: ID! + content: String +} diff --git a/packages/amplify-e2e-tests/schemas/simple_model_new_primary_key.graphql b/packages/amplify-e2e-tests/schemas/simple_model_new_primary_key.graphql deleted file mode 100644 index cdbe2826bfe..00000000000 --- a/packages/amplify-e2e-tests/schemas/simple_model_new_primary_key.graphql +++ /dev/null @@ -1,4 +0,0 @@ -type Todo @model @key(fields: ["content"]) { - id: ID! - content: String! -} diff --git a/packages/amplify-e2e-tests/src/__tests__/api_1.test.ts b/packages/amplify-e2e-tests/src/__tests__/api_1.test.ts index e20b226cab2..70e6c11e198 100644 --- a/packages/amplify-e2e-tests/src/__tests__/api_1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/api_1.test.ts @@ -4,7 +4,8 @@ import { deleteProject, initFlutterProjectWithProfile, initJSProjectWithProfile, - addApiWithSchema, + addApiWithBlankSchema, + addApiWithoutSchema, updateApiSchema, updateApiWithMultiAuth, createNewProjectDir, @@ -22,6 +23,7 @@ import { getBackendAmplifyMeta, amplifyPushUpdateForDependentModel, amplifyPushForce, + updateSchema, } from 'amplify-e2e-core'; import path from 'path'; import { existsSync } from 'fs'; @@ -30,8 +32,10 @@ import _ from 'lodash'; describe('amplify add api (GraphQL)', () => { let projRoot: string; + let projFolderName: string; beforeEach(async () => { - projRoot = await createNewProjectDir('graphql-api'); + projFolderName = 'graphqlapi'; + projRoot = await createNewProjectDir(projFolderName); }); afterEach(async () => { @@ -44,8 +48,10 @@ describe('amplify add api (GraphQL)', () => { it('init a project and add the simple_model api', async () => { const envName = 'devtest'; - await initJSProjectWithProfile(projRoot, { name: 'simplemodel', envName }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + const projName = 'simplemodel'; + await initJSProjectWithProfile(projRoot, { name: projName, envName }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projName, 'simple_model.graphql'); await amplifyPush(projRoot); const meta = getProjectMeta(projRoot); @@ -74,8 +80,10 @@ describe('amplify add api (GraphQL)', () => { it('init a project then add and remove api', async () => { const envName = 'devtest'; - await initIosProjectWithProfile(projRoot, { name: 'simplemodel', envName }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + const projName = 'simplemodel'; + await initIosProjectWithProfile(projRoot, { name: projName, envName }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projName, 'simple_model.graphql'); await amplifyPush(projRoot); let meta = getProjectMeta(projRoot); @@ -98,8 +106,10 @@ describe('amplify add api (GraphQL)', () => { it('init a Flutter project and add the simple_model api', async () => { const envName = 'devtest'; - await initFlutterProjectWithProfile(projRoot, { name: 'simplemodel', envName }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + const projName = 'simplemodel'; + await initFlutterProjectWithProfile(projRoot, { name: projName, envName }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projName, 'simple_model.graphql'); await amplifyPushWithoutCodegen(projRoot); const meta = getProjectMeta(projRoot); @@ -132,7 +142,8 @@ describe('amplify add api (GraphQL)', () => { const initialSchema = 'initial_key_blog.graphql'; const nextSchema = 'next_key_blog.graphql'; await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema); await amplifyPushUpdate(projRoot); @@ -146,7 +157,8 @@ describe('amplify add api (GraphQL)', () => { it('init a project and add the simple_model api with multiple authorization providers', async () => { await initJSProjectWithProfile(projRoot, { name: 'simplemodelmultiauth' }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'simplemodelmultiauth', 'simple_model.graphql'); await updateApiWithMultiAuth(projRoot, {}); await amplifyPush(projRoot); @@ -189,7 +201,8 @@ describe('amplify add api (GraphQL)', () => { it('init a project and add the simple_model api, match transformer version to current version', async () => { const name = `simplemodelv${TRANSFORM_CURRENT_VERSION}`; await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); await amplifyPush(projRoot); const meta = getProjectMeta(projRoot); @@ -217,7 +230,8 @@ describe('amplify add api (GraphQL)', () => { const initialSchema = 'two-model-schema.graphql'; const fnName = `integtestfn${random}`; await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await addFunction( projRoot, { @@ -264,7 +278,8 @@ describe('amplify add api (GraphQL)', () => { it('api force push with no changes', async () => { const projectName = `apinochange`; await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, 'two-model-schema.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, 'two-model-schema.graphql'); await amplifyPush(projRoot); let meta = getBackendAmplifyMeta(projRoot); const { lastPushDirHash: beforeDirHash } = meta.api[projectName]; diff --git a/packages/amplify-e2e-tests/src/__tests__/api_2.test.ts b/packages/amplify-e2e-tests/src/__tests__/api_2.test.ts index 1b8b5d639b7..1a1310e39bd 100644 --- a/packages/amplify-e2e-tests/src/__tests__/api_2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/api_2.test.ts @@ -1,37 +1,37 @@ import { + addApiWithBlankSchemaAndConflictDetection, + addApiWithoutSchema, + addFunction, + addRestApi, + addSimpleDDB, amplifyPush, - amplifyPushUpdate, - deleteProject, + amplifyPushUpdate, + apiDisableDataStore, + apiEnableDataStore, + checkIfBucketExists, + createNewProjectDir, + deleteProject, + deleteProjectDir, + enableAdminUI, + getAppSyncApi, + getLocalEnvInfo, + getProjectMeta, + getTransformConfig, initJSProjectWithProfile, listAttachedRolePolicies, listRolePolicies, + updateApiSchema, + updateAPIWithResolutionStrategy, updateAuthAddAdminQueries, } from 'amplify-e2e-core'; -import * as path from 'path'; -import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs'; import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync'; +import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'; import gql from 'graphql-tag'; -const providerName = 'awscloudformation'; - -import { - addApiWithSchema, - addApiWithSchemaAndConflictDetection, - addRestApi, - updateAPIWithResolutionStrategy, - apiUpdateToggleDataStore, - addFunction, - addSimpleDDB, - checkIfBucketExists, - createNewProjectDir, - deleteProjectDir, - getAppSyncApi, - getProjectMeta, - getLocalEnvInfo, - getTransformConfig, - enableAdminUI, -} from 'amplify-e2e-core'; import { TRANSFORM_CURRENT_VERSION } from 'graphql-transformer-core'; import _ from 'lodash'; +import * as path from 'path'; +const providerName = 'awscloudformation'; + // to deal with bug in cognito-identity-js (global as any).fetch = require('node-fetch'); @@ -55,7 +55,8 @@ describe('amplify add api (GraphQL)', () => { it('init a project with conflict detection enabled and a schema with @key, test update mutation', async () => { const name = `keyconflictdetection`; await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchemaAndConflictDetection(projRoot, 'key-conflict-detection.graphql'); + await addApiWithBlankSchemaAndConflictDetection(projRoot); + await updateApiSchema(projRoot, name, 'key-conflict-detection.graphql'); await amplifyPush(projRoot); const meta = getProjectMeta(projRoot); @@ -139,7 +140,8 @@ describe('amplify add api (GraphQL)', () => { it('init a project with conflict detection enabled and toggle disable', async () => { const name = `conflictdetection`; await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchemaAndConflictDetection(projRoot, 'simple_model.graphql'); + await addApiWithBlankSchemaAndConflictDetection(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); await amplifyPush(projRoot); @@ -165,7 +167,7 @@ describe('amplify add api (GraphQL)', () => { expect(transformConfig.ResolverConfig.project.ConflictHandler).toEqual('AUTOMERGE'); // remove datastore feature - await apiUpdateToggleDataStore(projRoot, {}); + await apiDisableDataStore(projRoot, {}); await amplifyPushUpdate(projRoot); const disableDSConfig = getTransformConfig(projRoot, name); expect(disableDSConfig).toBeDefined(); @@ -188,7 +190,8 @@ describe('amplify add api (GraphQL)', () => { // setupAdminUI await enableAdminUI(appId, envName, region); - await addApiWithSchemaAndConflictDetection(projRoot, 'simple_model.graphql'); + await addApiWithBlankSchemaAndConflictDetection(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); await amplifyPush(projRoot); meta = getProjectMeta(projRoot); @@ -209,7 +212,8 @@ describe('amplify add api (GraphQL)', () => { it('init a sync enabled project and update conflict resolution strategy', async () => { const name = `syncenabled`; await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchemaAndConflictDetection(projRoot, 'simple_model.graphql'); + await addApiWithBlankSchemaAndConflictDetection(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); let transformConfig = getTransformConfig(projRoot, name); expect(transformConfig).toBeDefined(); @@ -246,7 +250,8 @@ describe('amplify add api (GraphQL)', () => { it('init a datastore enabled project and then remove datastore config in update', async () => { const name = 'withoutdatastore'; await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); await amplifyPush(projRoot); const meta = getProjectMeta(projRoot); @@ -267,7 +272,7 @@ describe('amplify add api (GraphQL)', () => { expect(_.isEmpty(withoutDSConfig.ResolverConfig)).toBe(true); // amplify update api to enable datastore - await apiUpdateToggleDataStore(projRoot, {}); + await apiEnableDataStore(projRoot, {}); let transformConfigWithDS = getTransformConfig(projRoot, name); expect(transformConfigWithDS).toBeDefined(); expect(transformConfigWithDS.ResolverConfig).toBeDefined(); diff --git a/packages/amplify-e2e-tests/src/__tests__/api_3.test.ts b/packages/amplify-e2e-tests/src/__tests__/api_3.test.ts index 266fafd3423..6a426c246a7 100644 --- a/packages/amplify-e2e-tests/src/__tests__/api_3.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/api_3.test.ts @@ -8,7 +8,8 @@ import { updateHeadlessApi, getProjectSchema, removeHeadlessApi, - addApiWithSchema, + addApiWithoutSchema, + updateApiSchema, createNewProjectDir, deleteProjectDir, getAppSyncApi, @@ -39,8 +40,8 @@ describe('amplify add api (GraphQL)', () => { it('init a project and add the simple_model api, change transformer version to base version and push', async () => { const name = `simplemodelv${TRANSFORM_BASE_VERSION}`; await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); - + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); const transformConfig = getTransformConfig(projRoot, name); expect(transformConfig).toBeDefined(); expect(transformConfig.Version).toBeDefined(); diff --git a/packages/amplify-e2e-tests/src/__tests__/api_4.test.ts b/packages/amplify-e2e-tests/src/__tests__/api_4.test.ts index 177f658d14e..338fac8d53a 100644 --- a/packages/amplify-e2e-tests/src/__tests__/api_4.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/api_4.test.ts @@ -1,5 +1,6 @@ import { - addApiWithSchema, + addApiWithoutSchema, + updateApiSchema, amplifyPush, createNewProjectDir, deleteProject, @@ -31,7 +32,8 @@ describe('multi-key GSI behavior', () => { beforeEach(async () => { projRoot = await createNewProjectDir(projName); await initJSProjectWithProfile(projRoot, { name: projName }); - await addApiWithSchema(projRoot, 'multi-gsi.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projName, 'multi-gsi.graphql'); await amplifyPush(projRoot); appSyncClient = getAppSyncClientFromProj(projRoot); diff --git a/packages/amplify-e2e-tests/src/__tests__/api_5.test.ts b/packages/amplify-e2e-tests/src/__tests__/api_5.test.ts index dda41cf1685..d2899ec3ae8 100644 --- a/packages/amplify-e2e-tests/src/__tests__/api_5.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/api_5.test.ts @@ -1,84 +1,79 @@ import { - createNewProjectDir, - initJSProjectWithProfile, - addApiWithSchema, amplifyPush, + amplifyPushGraphQlWithCognitoPrompt, deleteProject, + initJSProjectWithProfile, + addApiWithoutSchema, + addApiWithAllAuthModesV2, + createNewProjectDir, deleteProjectDir, - putItemInTable, - scanTable, - rebuildApi, + getAppSyncApi, getProjectMeta, + addFeatureFlag, updateApiSchema, - amplifyPushDestructiveApiUpdate, - addFunction, - amplifyPushAuth, } from 'amplify-e2e-core'; +import path from 'path'; +import { existsSync } from 'fs'; +import _ from 'lodash'; -const projName = 'apitest'; -let projRoot; -beforeEach(async () => { - projRoot = await createNewProjectDir(projName); - await initJSProjectWithProfile(projRoot, { name: projName }); - await addApiWithSchema(projRoot, 'simple_model.graphql', { apiKeyExpirationDays: 7 }); - await amplifyPush(projRoot); -}); -afterEach(async () => { - await deleteProject(projRoot); - deleteProjectDir(projRoot); -}); +describe('test graphql lambda authorizer and auto apply auth mode', () => { + let projRoot: string; + let projFolderName: string; + beforeEach(async () => { + projFolderName = 'graphqlapi'; + projRoot = await createNewProjectDir(projFolderName); + }); -describe('amplify reset api', () => { - it('recreates all model tables', async () => { - const projMeta = getProjectMeta(projRoot); - const apiId = projMeta?.api?.[projName]?.output?.GraphQLAPIIdOutput; - const region = projMeta?.providers?.awscloudformation?.Region; - expect(apiId).toBeDefined(); - expect(region).toBeDefined(); - const tableName = `Todo-${apiId}-integtest`; - await putItemInTable(tableName, region, { id: 'this is a test value' }); - const scanResultBefore = await scanTable(tableName, region); - expect(scanResultBefore.Items.length).toBe(1); + afterEach(async () => { + const metaFilePath = path.join(projRoot, 'amplify', '#current-cloud-backend', 'amplify-meta.json'); + if (existsSync(metaFilePath)) { + await deleteProject(projRoot); + } + deleteProjectDir(projRoot); + }); - await rebuildApi(projRoot, projName); + // it('amplify add graphql api with lambda auth mode', async () => { + // const envName = 'devtest'; + // const projName = 'lambdaauthmode'; + // await initJSProjectWithProfile(projRoot, { name: projName, envName }); + // await addFeatureFlag(projRoot, 'graphqltransformer', 'useexperimentalpipelinedtransformer', true); + // await addApiWithAllAuthModesV2(projRoot); + // await amplifyPush(projRoot); - const scanResultAfter = await scanTable(tableName, region); - expect(scanResultAfter.Items.length).toBe(0); - }); -}); + // const meta = getProjectMeta(projRoot); + // const region = meta.providers.awscloudformation.Region; + // const { output } = meta.api.lambdaauthmode; + // const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; + // const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, region); -describe('destructive updates flag', () => { - it('blocks destructive updates when flag not present', async () => { - updateApiSchema(projRoot, projName, 'simple_model_new_primary_key.graphql'); - await amplifyPushDestructiveApiUpdate(projRoot, false); - // success indicates that the command errored out - }); + // expect(GraphQLAPIIdOutput).toBeDefined(); + // expect(GraphQLAPIEndpointOutput).toBeDefined(); + // expect(GraphQLAPIKeyOutput).toBeDefined(); - it('allows destructive updates when flag present', async () => { - updateApiSchema(projRoot, projName, 'simple_model_new_primary_key.graphql'); - await amplifyPushDestructiveApiUpdate(projRoot, true); - // success indicates that the push completed - }); + // expect(graphqlApi).toBeDefined(); + // expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput); + // }); + + it('amplify push prompt for cognito configuration if auth mode is missing', async () => { + const envName = 'devtest'; + const projName = 'lambdaauthmode'; + await initJSProjectWithProfile(projRoot, { name: projName, envName }); + await addFeatureFlag(projRoot, 'graphqltransformer', 'useexperimentalpipelinedtransformer', true); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projName, 'cognito_simple_model.graphql'); + await amplifyPushGraphQlWithCognitoPrompt(projRoot); + + const meta = getProjectMeta(projRoot); + const region = meta.providers.awscloudformation.Region; + const { output } = meta.api.lambdaauthmode; + const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; + const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, region); + + expect(GraphQLAPIIdOutput).toBeDefined(); + expect(GraphQLAPIEndpointOutput).toBeDefined(); + expect(GraphQLAPIKeyOutput).toBeDefined(); - it('disconnects and reconnects functions dependent on replaced table', async () => { - const functionName = 'funcTableDep'; - await addFunction( - projRoot, - { - name: functionName, - functionTemplate: 'Hello World', - additionalPermissions: { - permissions: ['storage'], - choices: ['api', 'storage'], - resources: ['Todo:@model(appsync)'], - operations: ['create', 'read', 'update', 'delete'], - }, - }, - 'nodejs', - ); - await amplifyPushAuth(projRoot); - updateApiSchema(projRoot, projName, 'simple_model_new_primary_key.graphql'); - await amplifyPushDestructiveApiUpdate(projRoot, false); - // success indicates that the push completed + expect(graphqlApi).toBeDefined(); + expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput); }); }); diff --git a/packages/amplify-e2e-tests/src/__tests__/feature-flags.test.ts b/packages/amplify-e2e-tests/src/__tests__/feature-flags.test.ts index 14cf60fab36..9f68cbe9a58 100644 --- a/packages/amplify-e2e-tests/src/__tests__/feature-flags.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/feature-flags.test.ts @@ -1,5 +1,5 @@ import * as fs from 'fs-extra'; -import { initJSProjectWithProfile, deleteProject, addApiWithSchema, amplifyPush, amplifyPull, getAppId } from 'amplify-e2e-core'; +import { initJSProjectWithProfile, deleteProject, addApiWithoutSchema, updateApiSchema, amplifyPush, amplifyPull, getAppId } from 'amplify-e2e-core'; import { createNewProjectDir, deleteProjectDir } from 'amplify-e2e-core'; import { pathManager } from 'amplify-cli-core'; import { addEnvironment } from '../environment/env'; @@ -24,9 +24,11 @@ describe('feature flags', () => { it('push and pull with multiple config files for environments', async () => { await initJSProjectWithProfile(projRoot, { + name: 'apifeatureflag', disableAmplifyAppCreation: false, }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'apifeatureflag', 'simple_model.graphql'); const envName = 'test'; const cliJSONPath = pathManager.getCLIJSONFilePath(projRoot); diff --git a/packages/amplify-e2e-tests/src/__tests__/function_1.test.ts b/packages/amplify-e2e-tests/src/__tests__/function_1.test.ts index 793eebb1f93..391a9ddb32b 100644 --- a/packages/amplify-e2e-tests/src/__tests__/function_1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/function_1.test.ts @@ -3,7 +3,7 @@ import { addFunction, functionBuild, addLambdaTrigger } from 'amplify-e2e-core'; import { addSimpleDDB } from 'amplify-e2e-core'; import { addKinesis } from 'amplify-e2e-core'; import { createNewProjectDir, deleteProjectDir, getProjectMeta, getFunction } from 'amplify-e2e-core'; -import { addApiWithSchema } from 'amplify-e2e-core'; +import { addApiWithoutSchema, updateApiSchema } from 'amplify-e2e-core'; import { appsyncGraphQLRequest } from 'amplify-e2e-core'; import { getCloudWatchLogs, putKinesisRecords, invokeFunction, getEventSourceMappings } from 'amplify-e2e-core'; @@ -60,8 +60,11 @@ describe('nodejs', () => { }); it('graphql mutation should result in trigger called in minimal AppSync + trigger infra', async () => { - await initJSProjectWithProfile(projRoot, {}); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await initJSProjectWithProfile(projRoot, { + name: 'graphqltriggerinfra', + }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'graphqltriggerinfra', 'simple_model.graphql'); await addFunction(projRoot, { functionTemplate: 'Lambda trigger', triggerType: 'DynamoDB' }, 'nodejs', addLambdaTrigger); await functionBuild(projRoot, {}); diff --git a/packages/amplify-e2e-tests/src/__tests__/function_2.test.ts b/packages/amplify-e2e-tests/src/__tests__/function_2.test.ts index d06803a3a57..e680f352e9a 100644 --- a/packages/amplify-e2e-tests/src/__tests__/function_2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/function_2.test.ts @@ -1,5 +1,6 @@ import { - addApiWithSchema, + addApiWithoutSchema, + updateApiSchema, addApi, addAuthWithDefault, addDDBWithTrigger, @@ -120,14 +121,17 @@ describe('nodejs', () => { }); it('lambda with dynamoDB permissions should be able to scan ddb', async () => { - await initJSProjectWithProfile(projRoot, {}); + await initJSProjectWithProfile(projRoot, { + name: 'dynamodbscan', + }); const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; const ddbName = `integtestddb${random}`; // test ability to scan both appsync @model-backed and regular ddb tables - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'dynamodbscan', 'simple_model.graphql'); await addSimpleDDB(projRoot, { name: ddbName }); await addFunction( @@ -185,7 +189,9 @@ describe('nodejs', () => { }); it('existing lambda updated with additional permissions should be able to scan ddb', async () => { - await initJSProjectWithProfile(projRoot, {}); + await initJSProjectWithProfile(projRoot, { + name: 'lambdaadditionalpermissions', + }); const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; @@ -209,7 +215,8 @@ describe('nodejs', () => { expect(functionName).toBeDefined(); expect(region).toBeDefined(); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'lambdaadditionalpermissions', 'simple_model.graphql'); await updateFunction( projRoot, { @@ -241,8 +248,11 @@ describe('nodejs', () => { }); it('@model-backed lambda function should generate envvars TODOTABLE_NAME, TODOTABLE_ARN, GRAPHQLAPIIDOUTPUT', async () => { - await initJSProjectWithProfile(projRoot, {}); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await initJSProjectWithProfile(projRoot, { + name: 'modelbackedlambda', + }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'modelbackedlambda', 'simple_model.graphql'); const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; @@ -304,8 +314,11 @@ describe('nodejs', () => { }); it('adding api and storage permissions should not add duplicates to CFN', async () => { - await initJSProjectWithProfile(projRoot, {}); - await addApiWithSchema(projRoot, 'two-model-schema.graphql'); + await initJSProjectWithProfile(projRoot, { + name: 'apistoragenoduplicate', + }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'apistoragenoduplicate', 'two-model-schema.graphql'); const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; @@ -367,8 +380,11 @@ describe('nodejs', () => { }); it('function dependencies should be preserved when not editing permissions during `amplify update function`', async () => { - await initJSProjectWithProfile(projRoot, {}); - await addApiWithSchema(projRoot, 'two-model-schema.graphql'); + await initJSProjectWithProfile(projRoot, { + name: 'functiondependenciespermission', + }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'functiondependenciespermission', 'two-model-schema.graphql'); const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; diff --git a/packages/amplify-e2e-tests/src/__tests__/function_5.test.ts b/packages/amplify-e2e-tests/src/__tests__/function_5.test.ts index a12eeb99fa0..9bb8ebdd115 100644 --- a/packages/amplify-e2e-tests/src/__tests__/function_5.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/function_5.test.ts @@ -1,6 +1,6 @@ import { addFunction, - addApiWithSchema, + addApiWithoutSchema, amplifyPull, amplifyPushAuth, amplifyPush, @@ -83,9 +83,13 @@ describe('test dependency in root stack', () => { }); it('init a project with api and function and update the @model and add function access to @model ', async () => { - await initJSProjectWithProfile(projRoot, {}); const projectName = 'mytestapi'; - await addApiWithSchema(projRoot, 'simple_model.graphql', { apiName: projectName }); + await initJSProjectWithProfile(projRoot, { + name: projectName, + }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, 'simple_model.graphql'); + const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; await addFunction( diff --git a/packages/amplify-e2e-tests/src/__tests__/migration/api.connection.migration.test.ts b/packages/amplify-e2e-tests/src/__tests__/migration/api.connection.migration.test.ts index e02ea98cc26..07eb0ccdd2d 100644 --- a/packages/amplify-e2e-tests/src/__tests__/migration/api.connection.migration.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/migration/api.connection.migration.test.ts @@ -1,5 +1,5 @@ import { initJSProjectWithProfile, deleteProject, amplifyPush, amplifyPushUpdate, addFeatureFlag } from 'amplify-e2e-core'; -import { addApiWithSchema, updateApiSchema } from 'amplify-e2e-core'; +import { addApiWithoutSchema, updateApiSchema } from 'amplify-e2e-core'; import { createNewProjectDir, deleteProjectDir } from 'amplify-e2e-core'; describe('amplify add api', () => { @@ -21,7 +21,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); await expect( @@ -40,7 +41,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); await expect( @@ -59,7 +61,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); await expect( @@ -79,7 +82,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); await amplifyPushUpdate(projRoot, /GraphQL endpoint:.*/); diff --git a/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration1.test.ts b/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration1.test.ts index d323a06dac6..2664f430aa7 100644 --- a/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration1.test.ts @@ -1,5 +1,5 @@ import { initJSProjectWithProfile, deleteProject, amplifyPush, amplifyPushUpdate, addFeatureFlag } from 'amplify-e2e-core'; -import { addApiWithSchema, updateApiSchema } from 'amplify-e2e-core'; +import { addApiWithoutSchema, updateApiSchema } from 'amplify-e2e-core'; import { createNewProjectDir, deleteProjectDir } from 'amplify-e2e-core'; describe('amplify add api', () => { @@ -23,7 +23,8 @@ describe('amplify add api', () => { // testing this with old behavior with named lsi key addFeatureFlag(projRoot, 'graphqltransformer', 'secondarykeyasgsi', false); - await addApiWithSchema(projRoot, initialSchema, { apiKeyExpirationDays: 2 }); + await addApiWithoutSchema(projRoot, { apiKeyExpirationDays: 2 }); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); @@ -43,7 +44,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); @@ -60,7 +62,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); @@ -77,7 +80,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); diff --git a/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts b/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts index eb7f82e437b..724026221f8 100644 --- a/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts @@ -1,5 +1,5 @@ import { initJSProjectWithProfile, deleteProject, amplifyPush, amplifyPushUpdate, addFeatureFlag } from 'amplify-e2e-core'; -import { addApiWithSchema, updateApiSchema, getProjectMeta } from 'amplify-e2e-core'; +import { addApiWithoutSchema, updateApiSchema, getProjectMeta } from 'amplify-e2e-core'; import { createNewProjectDir, deleteProjectDir } from 'amplify-e2e-core'; import { addEnvironment } from '../../environment/env'; @@ -19,7 +19,8 @@ describe('amplify add api', () => { const initial_schema = 'migrations_key/three_gsi_model_schema.graphql'; const nextSchema = 'migrations_key/four_gsi_model_schema.graphql'; await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initial_schema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initial_schema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema); @@ -32,7 +33,8 @@ describe('amplify add api', () => { const nextSchema1 = 'migrations_key/cant_change_key_schema.graphql'; await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); await addEnvironment(projRoot, { envName: 'test' }); updateApiSchema(projRoot, projectName, nextSchema1); @@ -53,7 +55,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); @@ -73,7 +76,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); @@ -93,7 +97,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); await expect( @@ -113,7 +118,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema); @@ -133,7 +139,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema); @@ -148,7 +155,8 @@ describe('amplify add api', () => { await initJSProjectWithProfile(projRoot, { name: projectName }); addFeatureFlag(projRoot, 'graphqltransformer', 'enableiterativegsiupdates', false); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); updateApiSchema(projRoot, projectName, nextSchema1); diff --git a/packages/amplify-e2e-tests/src/__tests__/pull.test.ts b/packages/amplify-e2e-tests/src/__tests__/pull.test.ts index eaba219a335..155ad825f7d 100644 --- a/packages/amplify-e2e-tests/src/__tests__/pull.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/pull.test.ts @@ -1,6 +1,7 @@ import { getAppId, - addApiWithSchema, + addApiWithoutSchema, + updateApiSchema, amplifyPull, amplifyPush, createNewProjectDir, @@ -24,8 +25,12 @@ describe('amplify pull', () => { }); it('pulling twice with noUpdateBackend does not re-prompt', async () => { - await initJSProjectWithProfile(projRoot, { disableAmplifyAppCreation: false }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await initJSProjectWithProfile(projRoot, { + disableAmplifyAppCreation: false, + name: 'testapi', + }); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'testapi', 'simple_model.graphql'); await amplifyPush(projRoot); const appId = getAppId(projRoot); await amplifyPull(projRoot2, { appId, emptyDir: true, noUpdateBackend: true }); diff --git a/packages/amplify-e2e-tests/src/__tests__/sandbox-mode.test.ts b/packages/amplify-e2e-tests/src/__tests__/sandbox-mode.test.ts new file mode 100644 index 00000000000..f16ac9e5cee --- /dev/null +++ b/packages/amplify-e2e-tests/src/__tests__/sandbox-mode.test.ts @@ -0,0 +1,45 @@ +import { + initJSProjectWithProfile, + deleteProject, + createNewProjectDir, + deleteProjectDir, + addApiWithSchema, + amplifyPush, + getProjectMeta, +} from 'amplify-e2e-core'; +import { testSchema } from '../schema-api-directives'; + +describe('api directives @allow_public_data_access_with_api_key', () => { + let projectDir: string; + const envName = 'dev'; + + beforeEach(async () => { + projectDir = await createNewProjectDir('model'); + await initJSProjectWithProfile(projectDir, { envName }); + }); + + afterEach(async () => { + await deleteProject(projectDir); + deleteProjectDir(projectDir); + }); + + it('schema and files generate with sandbox mode', async () => { + await addApiWithSchema(projectDir, 'model_with_sandbox_mode.graphql'); + await amplifyPush(projectDir); + + const meta = getProjectMeta(projectDir); + const { output } = meta.api.simplemodel; + const { authConfig, globalSandboxModeConfig, GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; + + expect(globalSandboxModeConfig.env).toBeDefined(); + expect(authConfig.defaultAuthentication.authenticationType).toBe('API_KEY'); + expect(authConfig.defaultAuthentication.apiKeyConfig.apiKeyExpirationDate).toBeDefined(); + + expect(GraphQLAPIIdOutput).toBeDefined(); + expect(GraphQLAPIEndpointOutput).toBeDefined(); + expect(GraphQLAPIKeyOutput).toBeDefined(); + + const testresult = await testSchema(projectDir, 'model', 'generates'); + expect(testresult).toBeTruthy(); + }); +}); diff --git a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-1.test.ts b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-1.test.ts index dd64161cb29..087d98f43e0 100644 --- a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-1.test.ts @@ -7,7 +7,7 @@ import { amplifyPushIterativeRollback, getDDBTable, getBackendAmplifyMeta, - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, updateApiSchema, @@ -22,7 +22,9 @@ describe('Iterative Rollback - add 2 @keys ', () => { beforeAll(async () => { projectDir = await createNewProjectDir('iterativeRollback'); - await initJSProjectWithProfile(projectDir, {}); + await initJSProjectWithProfile(projectDir, { + name: 'iterativerollbackaddkeys', + }); addFeatureFlag(projectDir, 'graphqltransformer', 'enableiterativegsiupdates', true); }); afterAll(async () => { @@ -30,9 +32,10 @@ describe('Iterative Rollback - add 2 @keys ', () => { deleteProjectDir(projectDir); }); it('should support rolling back from the 2nd deployment on adding gsis', async () => { - const apiName = 'renamekey'; + const apiName = 'iterativerollbackaddkeys'; const initialSchema = path.join('iterative-push', 'two-key-add', 'initial-schema.graphql'); - await addApiWithSchema(projectDir, initialSchema, { apiName, apiKeyExpirationDays: 7 }); + await addApiWithoutSchema(projectDir, { apiKeyExpirationDays: 7 }); + await updateApiSchema(projectDir, apiName, initialSchema); await amplifyPush(projectDir); // get info on table diff --git a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-2.test.ts b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-2.test.ts index fda5124dc18..eacffb877b2 100644 --- a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-rollback-2.test.ts @@ -7,7 +7,7 @@ import { amplifyPushIterativeRollback, getDDBTable, getBackendAmplifyMeta, - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, updateApiSchema, @@ -21,7 +21,9 @@ describe('Iterative Rollback - removing two @keys', () => { beforeAll(async () => { projectDir = await createNewProjectDir('iterativeRollback'); - await initJSProjectWithProfile(projectDir, {}); + await initJSProjectWithProfile(projectDir, { + name: 'iterativerollbackaddkeys', + }); addFeatureFlag(projectDir, 'graphqltransformer', 'enableiterativegsiupdates', true); }); afterAll(async () => { @@ -29,9 +31,10 @@ describe('Iterative Rollback - removing two @keys', () => { deleteProjectDir(projectDir); }); it('should support rolling back from the 2nd deployment on adding gsis', async () => { - const apiName = 'renamekey'; + const apiName = 'iterativerollbackaddkeys'; const initialSchema = path.join('iterative-push', 'multiple-key-delete', 'initial-schema.graphql'); - await addApiWithSchema(projectDir, initialSchema, { apiName, apiKeyExpirationDays: 7 }); + await addApiWithoutSchema(projectDir, { apiKeyExpirationDays: 7 }); + await updateApiSchema(projectDir, apiName, initialSchema); await amplifyPush(projectDir); // get info on table diff --git a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-1.test.ts b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-1.test.ts index 2c4109c2b8b..2de29858aea 100644 --- a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-1.test.ts @@ -4,7 +4,7 @@ import { initJSProjectWithProfile, deleteProject, deleteProjectDir, - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, updateApiSchema, @@ -16,7 +16,9 @@ describe('Schema iterative update - rename @key', () => { beforeAll(async () => { projectDir = await createNewProjectDir('schemaIterative'); - await initJSProjectWithProfile(projectDir, {}); + await initJSProjectWithProfile(projectDir, { + name: 'schemaiterativeupdate', + }); addFeatureFlag(projectDir, 'graphqltransformer', 'enableiterativegsiupdates', true); }); @@ -25,10 +27,11 @@ describe('Schema iterative update - rename @key', () => { deleteProjectDir(projectDir); }); it('should support changing gsi name', async () => { - const apiName = 'renamekey'; + const apiName = 'schemaiterativeupdate'; const initialSchema = path.join('iterative-push', 'change-model-name', 'initial-schema.graphql'); - await addApiWithSchema(projectDir, initialSchema, { apiName, apiKeyExpirationDays: 7 }); + await addApiWithoutSchema(projectDir, { apiKeyExpirationDays: 7 }); + await updateApiSchema(projectDir, apiName, initialSchema); await amplifyPush(projectDir); const finalSchema = path.join('iterative-push', 'change-model-name', 'final-schema.graphql'); diff --git a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-3.test.ts b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-3.test.ts index 31fd211f241..1878135d8ef 100644 --- a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-3.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-3.test.ts @@ -4,7 +4,7 @@ import { initJSProjectWithProfile, deleteProject, deleteProjectDir, - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, updateApiSchema, @@ -16,7 +16,9 @@ describe('Schema iterative update - delete', () => { beforeAll(async () => { projectDir = await createNewProjectDir('schemaIterative'); - await initJSProjectWithProfile(projectDir, {}); + await initJSProjectWithProfile(projectDir, { + name: 'deletekeys', + }); addFeatureFlag(projectDir, 'graphqltransformer', 'enableiterativegsiupdates', true); }); @@ -28,7 +30,8 @@ describe('Schema iterative update - delete', () => { const apiName = 'deletekeys'; const initialSchema = path.join('iterative-push', 'multiple-key-delete', 'initial-schema.graphql'); - await addApiWithSchema(projectDir, initialSchema, { apiName, apiKeyExpirationDays: 7 }); + await addApiWithoutSchema(projectDir, { apiKeyExpirationDays: 7 }); + await updateApiSchema(projectDir, apiName, initialSchema); await amplifyPush(projectDir); const finalSchema = path.join('iterative-push', 'multiple-key-delete', 'final-schema.graphql'); diff --git a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-4.test.ts b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-4.test.ts index a83805997a0..6f1a0bb1180 100644 --- a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-4.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-4.test.ts @@ -4,7 +4,7 @@ import { initJSProjectWithProfile, deleteProject, deleteProjectDir, - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, updateApiSchema, @@ -16,7 +16,9 @@ describe('Schema iterative update - create update and delete', () => { beforeAll(async () => { projectDir = await createNewProjectDir('schemaIterative'); - await initJSProjectWithProfile(projectDir, {}); + await initJSProjectWithProfile(projectDir, { + name: 'iterativetest1', + }); addFeatureFlag(projectDir, 'graphqltransformer', 'enableiterativegsiupdates', true); }); @@ -28,7 +30,8 @@ describe('Schema iterative update - create update and delete', () => { const apiName = 'iterativetest1'; const initialSchema = path.join('iterative-push', 'add-remove-and-update-key', 'initial-schema.graphql'); - await addApiWithSchema(projectDir, initialSchema, { apiName, apiKeyExpirationDays: 7 }); + await addApiWithoutSchema(projectDir, { apiKeyExpirationDays: 7 }); + await updateApiSchema(projectDir, apiName, initialSchema); await amplifyPush(projectDir); const finalSchema = path.join('iterative-push', 'add-remove-and-update-key', 'final-schema.graphql'); diff --git a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-locking.test.ts b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-locking.test.ts index 1fe5aefa131..d0ea8a4818b 100644 --- a/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-locking.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/schema-iterative-update-locking.test.ts @@ -4,7 +4,7 @@ import { initJSProjectWithProfile, deleteProject, deleteProjectDir, - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, updateApiSchema, @@ -24,6 +24,7 @@ describe('Schema iterative update - locking', () => { projectRoot = await createNewProjectDir('schemaIterativeLock'); await initJSProjectWithProfile(projectRoot, { + name: 'iterlock', disableAmplifyAppCreation: false, }); @@ -40,7 +41,8 @@ describe('Schema iterative update - locking', () => { // Create and push project with API const initialSchema = path.join('iterative-push', 'change-model-name', 'initial-schema.graphql'); - await addApiWithSchema(projectRoot, initialSchema, { apiName, apiKeyExpirationDays: 7 }); + await addApiWithoutSchema(projectRoot, { apiKeyExpirationDays: 7 }); + await updateApiSchema(projectRoot, apiName, initialSchema); await amplifyPush(projectRoot); // Apply updates to first project diff --git a/packages/amplify-e2e-tests/src/schema-api-directives/tests/key-howTo4.ts b/packages/amplify-e2e-tests/src/schema-api-directives/tests/key-howTo4.ts index 9c5398c0c14..d82cea99380 100644 --- a/packages/amplify-e2e-tests/src/schema-api-directives/tests/key-howTo4.ts +++ b/packages/amplify-e2e-tests/src/schema-api-directives/tests/key-howTo4.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { addApiWithSchemaAndConflictDetection, amplifyPush } from 'amplify-e2e-core'; +import { addApiWithBlankSchemaAndConflictDetection, addApiWithBlankSchema, amplifyPush, updateApiSchema } from 'amplify-e2e-core'; import { getApiKey, configureAmplify, getConfiguredAppsyncClientAPIKeyAuth } from '../authHelper'; import { testQueries, testMutations } from '../common'; @@ -270,7 +270,10 @@ export const expected_result_query5 = { }; export async function runTest(projectDir: string, testModule: any) { - await addApiWithSchemaAndConflictDetection(projectDir, testModule.schemaName); + await addApiWithBlankSchema(projectDir, { + apiName: 'testapi', + }); + await updateApiSchema(projectDir, 'testapi', testModule.schemaName); await amplifyPush(projectDir); const awsconfig = configureAmplify(projectDir); diff --git a/packages/amplify-frontend-ios/CHANGELOG.md b/packages/amplify-frontend-ios/CHANGELOG.md index d13f521663f..c4d9f8eb17c 100644 --- a/packages/amplify-frontend-ios/CHANGELOG.md +++ b/packages/amplify-frontend-ios/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.20.15-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-frontend-ios@2.20.14...amplify-frontend-ios@2.20.15-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-frontend-ios + + + + + ## [2.20.14](https://github.com/aws-amplify/amplify-cli/compare/amplify-frontend-ios@2.20.13...amplify-frontend-ios@2.20.14) (2021-09-18) **Note:** Version bump only for package amplify-frontend-ios diff --git a/packages/amplify-frontend-ios/package.json b/packages/amplify-frontend-ios/package.json index 6201bd7eede..a3647101bf7 100644 --- a/packages/amplify-frontend-ios/package.json +++ b/packages/amplify-frontend-ios/package.json @@ -1,6 +1,6 @@ { "name": "amplify-frontend-ios", - "version": "2.20.14", + "version": "2.20.15-graphql-vnext-dev-preview.0", "description": "amplify-cli front-end plugin for xcode projects", "repository": { "type": "git", @@ -21,7 +21,7 @@ "test-watch": "jest --watch" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "execa": "^5.1.1", "fs-extra": "^8.1.0", "graphql-config": "^2.2.1", diff --git a/packages/amplify-frontend-javascript/CHANGELOG.md b/packages/amplify-frontend-javascript/CHANGELOG.md index f73c6d95a36..7a149ff4c85 100644 --- a/packages/amplify-frontend-javascript/CHANGELOG.md +++ b/packages/amplify-frontend-javascript/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.24.2-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-frontend-javascript@2.24.1...amplify-frontend-javascript@2.24.2-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-frontend-javascript + + + + + ## [2.24.1](https://github.com/aws-amplify/amplify-cli/compare/amplify-frontend-javascript@2.24.0...amplify-frontend-javascript@2.24.1) (2021-09-18) **Note:** Version bump only for package amplify-frontend-javascript diff --git a/packages/amplify-frontend-javascript/package.json b/packages/amplify-frontend-javascript/package.json index fe84bb6f731..363edfc1926 100644 --- a/packages/amplify-frontend-javascript/package.json +++ b/packages/amplify-frontend-javascript/package.json @@ -1,6 +1,6 @@ { "name": "amplify-frontend-javascript", - "version": "2.24.1", + "version": "2.24.2-graphql-vnext-dev-preview.0", "description": "amplify-cli front-end plugin for JavaScript projects", "repository": { "type": "git", @@ -16,7 +16,7 @@ "aws" ], "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "chalk": "^4.1.1", "execa": "^5.1.1", "fs-extra": "^8.1.0", diff --git a/packages/amplify-go-function-runtime-provider/CHANGELOG.md b/packages/amplify-go-function-runtime-provider/CHANGELOG.md index 627567d0485..12d5730a44b 100644 --- a/packages/amplify-go-function-runtime-provider/CHANGELOG.md +++ b/packages/amplify-go-function-runtime-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.9.5-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-go-function-runtime-provider@1.9.4...amplify-go-function-runtime-provider@1.9.5-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-go-function-runtime-provider + + + + + ## [1.9.4](https://github.com/aws-amplify/amplify-cli/compare/amplify-go-function-runtime-provider@1.9.3...amplify-go-function-runtime-provider@1.9.4) (2021-09-18) **Note:** Version bump only for package amplify-go-function-runtime-provider diff --git a/packages/amplify-go-function-runtime-provider/package.json b/packages/amplify-go-function-runtime-provider/package.json index a9bb191c623..d69d5310c08 100644 --- a/packages/amplify-go-function-runtime-provider/package.json +++ b/packages/amplify-go-function-runtime-provider/package.json @@ -1,6 +1,6 @@ { "name": "amplify-go-function-runtime-provider", - "version": "1.9.4", + "version": "1.9.5-graphql-vnext-dev-preview.0", "description": "Provides functionality related to functions in Go 1.x on AWS", "repository": { "type": "git", @@ -21,7 +21,7 @@ "clean": "rimraf lib tsconfig.tsbuildinfo resources/localinvoke/go.sum resources/localinvoke/main resources/localinvoke/main.exe" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", "archiver": "^5.3.0", "execa": "^5.1.1", diff --git a/packages/amplify-graphql-auth-transformer/.npmignore b/packages/amplify-graphql-auth-transformer/.npmignore new file mode 100644 index 00000000000..3ee5d55b0b8 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/.npmignore @@ -0,0 +1,5 @@ +**/__mocks__/** +**/__tests__/** +src +tsconfig.json +tsconfig.tsbuildinfo diff --git a/packages/amplify-graphql-auth-transformer/CHANGELOG.md b/packages/amplify-graphql-auth-transformer/CHANGELOG.md new file mode 100644 index 00000000000..cb4f48a841e --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.2.0-graphql-vnext-dev-preview.0 (2021-09-27) + + +### Bug Fixes + +* update dependency versions ([b4cbe97](https://github.com/aws-amplify/amplify-cli/commit/b4cbe97ea09f392469b4b84727db297228daf129)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) diff --git a/packages/amplify-graphql-auth-transformer/package.json b/packages/amplify-graphql-auth-transformer/package.json new file mode 100644 index 00000000000..c61fca87851 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/package.json @@ -0,0 +1,67 @@ +{ + "name": "@aws-amplify/graphql-auth-transformer", + "version": "0.2.0-graphql-vnext-dev-preview.0", + "description": "Amplify GraphQL @auth Transformer", + "repository": { + "type": "git", + "url": "https://github.com/aws-amplify/amplify-cli.git", + "directory": "packages/amplify-graphql-auth-transformer" + }, + "author": "Amazon Web Services", + "license": "Apache-2.0", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "keywords": [ + "graphql", + "cloudformation", + "aws", + "amplify" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "test": "jest", + "build": "tsc", + "clean": "rimraf ./lib", + "watch": "tsc -w" + }, + "dependencies": { + "@aws-amplify/graphql-model-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", + "@aws-cdk/aws-appsync": "~1.124.0", + "@aws-cdk/aws-dynamodb": "~1.124.0", + "@aws-cdk/aws-iam": "~1.124.0", + "@aws-cdk/core": "~1.124.0", + "constructs": "^3.3.125", + "graphql": "^14.5.8", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@aws-amplify/graphql-index-transformer": "0.4.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-relational-transformer": "0.3.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-searchable-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-cdk/assert": "~1.124.0", + "@types/fs-extra": "^8.0.1", + "@types/node": "^12.12.6" + }, + "jest": { + "testURL": "http://localhost", + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "(src/__tests__/.*.test.ts)$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" + ], + "collectCoverage": true + } +} diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/__snapshots__/field-auth-argument.test.ts.snap b/packages/amplify-graphql-auth-transformer/src/__tests__/__snapshots__/field-auth-argument.test.ts.snap new file mode 100644 index 00000000000..0a0706bd49e --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/__snapshots__/field-auth-argument.test.ts.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`per-field @auth without @model 1`] = ` +Object { + "Properties": Object { + "Description": "", + "Path": "/", + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": Object { + "Fn::Sub": Array [ + "arn:aws:appsync:\${AWS::Region}:\${AWS::AccountId}:apis/\${apiId}/types/\${typeName}/fields/\${fieldName}", + Object { + "apiId": Object { + "Fn::GetAtt": Array [ + "GraphQLAPI", + "ApiId", + ], + }, + "fieldName": "listContext", + "typeName": "Query", + }, + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "Roles": Array [ + Object { + "Ref": "authRoleName", + }, + ], + }, + "Type": "AWS::IAM::ManagedPolicy", +} +`; diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/accesscontrol.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/accesscontrol.test.ts new file mode 100644 index 00000000000..6aa2d0e9ecf --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/accesscontrol.test.ts @@ -0,0 +1,112 @@ +import { AccessControlMatrix } from '../accesscontrol'; +import { MODEL_OPERATIONS } from '../utils'; + +test('test access control on object and field', () => { + /* + given the following schema + type Student + @model + @auth(rules: [ + { allow: groups, groups: ["admin"] } + { allow: groups, groups: ["student"], operations: [read] } + ]) { + studentID: ID + name: String + #acm protect email only studentID can update their own email + email: AWSEmail @auth(rules: [ + { allow: owner, ownerField: "studentID", operations: [update] } + { allow: groups, groups: ["admin"] } + ]) + # only allowed to student and admin + ssn: String @auth(rules: [ + { allow: owner, ownerField: "studentID", operations: [read] } + { allow: groups, groups: ["admin"] } + ]) + } + */ + // create an acm for the student type + const adminRole = 'userPools:staticGroup:admin'; + const studentGroupRole = 'userPools:staticGroup:student'; + const studentOwnerRole = 'userPools:owner:studentID'; + const studentTypeFields = ['studentID', 'name', 'email', 'ssn']; + const acm = new AccessControlMatrix({ + resources: studentTypeFields, + operations: MODEL_OPERATIONS, + }); + // add OBJECT rules first + // add admin role which has full access on all CRUD operations for all fields + acm.setRole({ + role: adminRole, + operations: MODEL_OPERATIONS, + }); + // add the student static group rule which only has read access + acm.setRole({ + role: studentGroupRole, + operations: ['read'], + }); + + studentTypeFields.forEach(field => { + // check that admin has CRUD access on all fields + expect(acm.isAllowed(adminRole, field, 'create')).toBe(true); + expect(acm.isAllowed(adminRole, field, 'read')).toBe(true); + expect(acm.isAllowed(adminRole, field, 'update')).toBe(true); + expect(acm.isAllowed(adminRole, field, 'delete')).toBe(true); + // check that studentGroupRole has access to read only + expect(acm.isAllowed(studentGroupRole, field, 'read')).toBe(true); + expect(acm.isAllowed(studentGroupRole, field, 'create')).toBe(false); + expect(acm.isAllowed(studentGroupRole, field, 'update')).toBe(false); + expect(acm.isAllowed(studentGroupRole, field, 'delete')).toBe(false); + }); + // when adding a field rule on email we need to overwrite it + acm.resetAccessForResource('email'); + + expect(acm.isAllowed(studentGroupRole, 'email', 'read')).toBe(false); + acm.setRole({ + role: studentOwnerRole, + operations: ['update'], + resource: 'email', + }); + expect(acm.isAllowed(adminRole, 'email', 'update')).toBe(false); + expect(acm.isAllowed(studentOwnerRole, 'email', 'update')).toBe(true); +}); + +test('test access control only on field', () => { + /* + given the following schema + type Student + @model { + studentID: ID + name: String + # only allows read access on email and ssn for studentID ownerfield can also only update email + email: AWSEmail @auth(rules: [ + { allow: owner, ownerField: "studentID", operations: [read, update] } + ]) + ssn: String @auth(rules: [ + { allow: owner, ownerField: "studentID", operations: [read] } + ]) + } + */ + // create an acm for the student type + const studentOwnerRole = 'userPools:owner:studentID'; + const studentTypeFields = ['studentID', 'name', 'email', 'ssn']; + const acm = new AccessControlMatrix({ + resources: studentTypeFields, + operations: MODEL_OPERATIONS, + }); + // set role for email field + acm.setRole({ role: studentOwnerRole, operations: ['read', 'update'], resource: 'email' }); + // set role for ssn field + acm.setRole({ role: studentOwnerRole, operations: ['read'], resource: 'ssn' }); + + // expect the correct permissions are assigned for email field + expect(acm.isAllowed(studentOwnerRole, 'email', 'update')).toBe(true); + expect(acm.isAllowed(studentOwnerRole, 'email', 'read')).toBe(true); + expect(acm.isAllowed(studentOwnerRole, 'email', 'delete')).toBe(false); + expect(acm.isAllowed(studentOwnerRole, 'email', 'create')).toBe(false); + + // expect the correct permissions are assigned for ssn field + expect(acm.isAllowed(studentOwnerRole, 'ssn', 'create')).toBe(false); + expect(acm.isAllowed(studentOwnerRole, 'ssn', 'read')).toBe(true); + expect(acm.isAllowed(studentOwnerRole, 'ssn', 'update')).toBe(false); + expect(acm.isAllowed(studentOwnerRole, 'ssn', 'delete')).toBe(false); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/amplify-admin-auth.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/amplify-admin-auth.test.ts new file mode 100644 index 00000000000..ce358a0143e --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/amplify-admin-auth.test.ts @@ -0,0 +1,337 @@ +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import _ from 'lodash'; + +test('test simple model with public auth rule and amplify admin app is present', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: public}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'API_KEY', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: true, + adminUserPoolID: 'us-fake-1_uuid', + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).toContain('Post @aws_api_key @aws_iam'); +}); + +test('Test simple model with public auth rule and amplify admin app is not enabled', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: public}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'API_KEY', + }, + additionalAuthenticationProviders: [], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).not.toContain('Post @aws_api_key @aws_iam'); +}); + +test('Test model with public auth rule without all operations and amplify admin app is present', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: public, operations: [read, update]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'API_KEY', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: true, + adminUserPoolID: 'us-fake-1_uuid', + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + + expect(out.schema).toContain('type Post @aws_iam @aws_api_key'); + expect(out.schema).toContain('createPost(input: CreatePostInput!, condition: ModelPostConditionInput): Post @aws_api_key @aws_iam'); + expect(out.schema).toContain('updatePost(input: UpdatePostInput!, condition: ModelPostConditionInput): Post @aws_api_key @aws_iam'); + expect(out.schema).toContain('deletePost(input: DeletePostInput!, condition: ModelPostConditionInput): Post @aws_api_key @aws_iam'); + + // No Resource extending Auth and UnAuth role + const policyResources = Object.values(out.rootStack.Resources!).filter(r => r.Type === 'AWS::IAM::ManagedPolicy'); + expect(policyResources).toHaveLength(0); +}); + +test('Test simple model with private auth rule and amplify admin app is present', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: groups, groups: ["Admin", "Dev"]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: true, + adminUserPoolID: 'us-fake-1_uuid', + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).toContain('type Post @aws_iam @aws_cognito_user_pools'); +}); + +test('Test simple model with private auth rule and amplify admin app not enabled', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: groups, groups: ["Admin", "Dev"]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).not.toContain('type Post @aws_iam @aws_cognito_user_pools'); +}); + +test('Test simple model with private auth rule, few operations, and amplify admin app enabled', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: groups, groups: ["Admin", "Dev"], operations: [read]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig: { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + addAwsIamAuthInOutputSchema: true, + adminUserPoolID: 'us-fake-1_uuid', + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).toContain('type Post @aws_iam @aws_cognito_user_pools'); + expect(out.schema).toContain( + 'createPost(input: CreatePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + expect(out.schema).toContain( + 'updatePost(input: UpdatePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + expect(out.schema).toContain( + 'deletePost(input: DeletePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + + // No Resource extending Auth and UnAuth role + const policyResources = Object.values(out.rootStack.Resources!).filter(r => r.Type === 'AWS::IAM::ManagedPolicy'); + expect(policyResources).toHaveLength(0); +}); + +test('Test simple model with private IAM auth rule, few operations, and amplify admin app is not enabled', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: private, provider: iam, operations: [read]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).toContain('Post @aws_iam'); + expect(out.schema).not.toContain( + 'createPost(input: CreatePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + expect(out.schema).not.toContain('deletePost(input: DeletePostInput!): Post @aws_iam'); + expect(out.schema).not.toContain('updatePost(input: UpdatePostInput!): Post @aws_iam'); + + expect(out.schema).toContain('getPost(id: ID!): Post @aws_iam'); + expect(out.schema).toContain('listPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String): ModelPostConnection @aws_iam'); +}); + +test('Test simple model with AdminUI enabled should add IAM policy only for fields that have explicit IAM auth', () => { + const validSchema = ` + type Post @model @auth(rules: [{allow: private, provider: iam, operations: [read]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig: { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_IAM', + }, + ], + }, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + addAwsIamAuthInOutputSchema: true, + adminUserPoolID: 'us-fake-1_uuid', + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).toContain('Post @aws_iam @aws_cognito_user_pool'); + expect(out.schema).toContain( + 'createPost(input: CreatePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + expect(out.schema).toContain( + 'updatePost(input: UpdatePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + expect(out.schema).toContain( + 'deletePost(input: DeletePostInput!, condition: ModelPostConditionInput): Post @aws_iam @aws_cognito_user_pools', + ); + + expect(out.schema).toContain('getPost(id: ID!): Post @aws_iam'); + expect(out.schema).toContain('listPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String): ModelPostConnection @aws_iam'); + const policyResources = _.filter(out.rootStack.Resources, r => r.Type === 'AWS::IAM::ManagedPolicy'); + expect(policyResources).toHaveLength(1); + const resources = _.get(policyResources, '[0].Properties.PolicyDocument.Statement[0].Resource'); + const typeFieldList = _.map(resources, r => _.get(r, 'Fn::Sub[1]')).map(r => `${_.get(r, 'typeName')}.${_.get(r, 'fieldName', '*')}`); + expect(typeFieldList).toEqual([ + 'Post.*', + 'Query.getPost', + 'Query.listPosts', + 'Mutation.createPost', + 'Mutation.updatePost', + 'Mutation.deletePost', + 'Subscription.onCreatePost', + 'Subscription.onUpdatePost', + 'Subscription.onDeletePost', + ]); + // should throw unauthorized if it's not signed by the admin ui iam role + ['Mutation.createPost.auth.1.req.vtl', 'Mutation.updatePost.auth.1.res.vtl', 'Mutation.deletePost.auth.1.res.vtl'].forEach(r => { + expect(out.pipelineFunctions[r]).toContain( + '#if( $util.authType() == "IAM Authorization" )\n' + + ' #if( $ctx.identity.userArn.contains("us-fake-1_uuid_Full-access/CognitoIdentityCredentials") || $ctx.identity.userArn.contains("us-fake-1_uuid_Manage-only/CognitoIdentityCredentials") )\n' + + ' #return($util.toJson({})\n' + + ' #end\n' + + '$util.unauthorized()\n' + + '#end', + ); + }); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/custom-auth.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/custom-auth.test.ts new file mode 100644 index 00000000000..7b06760c009 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/custom-auth.test.ts @@ -0,0 +1,137 @@ +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { ResourceConstants } from 'graphql-transformer-common'; +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; + +test('happy case with lambda auth mode as default auth mode', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AWS_LAMBDA', + lambdaAuthorizerConfig: { + lambdaFunction: 'testfunction', + ttlSeconds: 600, + }, + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [{ allow: custom, provider: function }]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.rootStack!.Resources![ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual('AWS_LAMBDA'); +}); + +test('happy case with lambda auth mode as additional auth mode', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'AWS_LAMBDA', + lambdaAuthorizerConfig: { + lambdaFunction: 'testfunction', + ttlSeconds: 600, + }, + }, + ], + }; + const validSchema = ` + type Post @model @auth(rules: [{ allow: custom, provider: function }]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.rootStack!.Resources![ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AdditionalAuthenticationProviders[0].AuthenticationType).toEqual('AWS_LAMBDA'); +}); + +test('allow: custom defaults provider to function', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AWS_LAMBDA', + lambdaAuthorizerConfig: { + lambdaFunction: 'testfunction', + ttlSeconds: 600, + }, + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [{ allow: custom }]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.rootStack!.Resources![ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual('AWS_LAMBDA'); +}); + +test('allow: custom error out when there is no lambda auth mode defined', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [{ allow: custom, provider: function }]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + expect(() => transformer.transform(validSchema)).toThrowError( + `@auth directive with 'function' provider found, but the project has no Lambda authentication provider configured.`, + ); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/field-auth-argument.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/field-auth-argument.test.ts new file mode 100644 index 00000000000..5a30bfda709 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/field-auth-argument.test.ts @@ -0,0 +1,118 @@ +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { HasManyTransformer } from '@aws-amplify/graphql-relational-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { ResourceConstants } from 'graphql-transformer-common'; +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; + +test('subscriptions are only generated if the respective mutation operation exists', () => { + const validSchema = ` + type Salary + @model + @auth(rules: [ + {allow: owner}, + {allow: groups, groups: ["Moderator"]} + ]) { + id: ID! + wage: Int + owner: String + secret: String @auth(rules: [{allow: owner}]) + }`; + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new HasManyTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + // expect to generate subscription resolvers for create and update only + expect(out).toBeDefined(); + expect(out.rootStack.Resources[ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual( + 'AMAZON_COGNITO_USER_POOLS', + ); + expect(out.pipelineFunctions['Salary.secret.res.vtl']).toContain('#if( $operation == "Mutation" )'); + + expect(out.pipelineFunctions['Mutation.createSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))'); + expect(out.pipelineFunctions['Mutation.updateSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))'); + expect(out.pipelineFunctions['Mutation.deleteSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))'); +}); + +test('per-field auth on relational field', () => { + const validSchema = ` + type Post @model @auth(rules: [ { allow: groups, groups: ["admin"] }, { allow: groups, groups: ["viewer"], operations: [read] } ]){ + id: ID! + title: String! + comments: [Comment] @hasMany @auth(rules: [ { allow: groups, groups: ["admin"] } ]) + } + + type Comment @model { + id: ID! + content: String + }`; + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [{ authenticationType: 'AWS_IAM' }], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new HasManyTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + + expect(out.pipelineFunctions['Post.comments.auth.1.req.vtl']).toContain( + '#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"admin"}] )', + ); +}); + +test('per-field @auth without @model', () => { + const validSchema = ` + type Query { + listContext: String @auth(rules: [{ allow: groups, groups: ["Allowed"] }, { allow: private, provider: iam }]) + }`; + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [{ authenticationType: 'AWS_IAM' }], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + + const resources = out.rootStack.Resources; + const authPolicyIdx = Object.keys(out.rootStack.Resources).find(r => r.includes('AuthRolePolicy')); + expect(resources[authPolicyIdx]).toMatchSnapshot(); + expect(out.pipelineFunctions['Query.listContext.req.vtl']).toContain( + '#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"Allowed"}] )', + ); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/group-auth.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/group-auth.test.ts new file mode 100644 index 00000000000..6bf8f548ac8 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/group-auth.test.ts @@ -0,0 +1,97 @@ +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { ResourceConstants } from 'graphql-transformer-common'; +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; + +test('happy case with static groups', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [{allow: groups, groups: ["Admin", "Dev"]}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.rootStack!.Resources![ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual( + 'AMAZON_COGNITO_USER_POOLS', + ); +}); + +test('happy case with dynamic groups', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [{allow: groups, groupsField: "groups"}]) { + id: ID! + title: String! + groups: [String] + createdAt: String + updatedAt: String + } + `; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.rootStack!.Resources![ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual( + 'AMAZON_COGNITO_USER_POOLS', + ); +}); + +test('validation on @auth on a non-@model type', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const invalidSchema = ` + type Post @auth(rules: [{allow: groups, groupsField: "groups"}]) { + id: ID! + title: String! + group: String + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + expect(() => transformer.transform(invalidSchema)).toThrowError('Types annotated with @auth must also be annotated with @model.'); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/multi-auth.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/multi-auth.test.ts new file mode 100644 index 00000000000..620824ea105 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/multi-auth.test.ts @@ -0,0 +1,683 @@ +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { IndexTransformer } from '@aws-amplify/graphql-index-transformer'; +import { HasManyTransformer, HasOneTransformer, BelongsToTransformer } from '@aws-amplify/graphql-relational-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { AppSyncAuthConfiguration, AppSyncAuthConfigurationOIDCEntry, AppSyncAuthMode } from '@aws-amplify/graphql-transformer-interfaces'; +import { DocumentNode, ObjectTypeDefinitionNode, Kind, FieldDefinitionNode, parse, InputValueDefinitionNode } from 'graphql'; + +const userPoolsDefaultConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], +}; + +const apiKeyDefaultConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'API_KEY', + }, + additionalAuthenticationProviders: [], +}; + +const iamDefaultConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AWS_IAM', + }, + additionalAuthenticationProviders: [], +}; + +const withAuthModes = (authConfig: AppSyncAuthConfiguration, authModes: AppSyncAuthMode[]): AppSyncAuthConfiguration => { + const newAuthConfig = { + defaultAuthentication: { + authenticationType: authConfig.defaultAuthentication.authenticationType, + }, + additionalAuthenticationProviders: [], + }; + + for (const authMode of authModes) { + newAuthConfig.additionalAuthenticationProviders.push({ + authenticationType: authMode, + }); + } + + return newAuthConfig; +}; + +const apiKeyDirectiveName = 'aws_api_key'; +const userPoolsDirectiveName = 'aws_cognito_user_pools'; +const iamDirectiveName = 'aws_iam'; +const openIdDirectiveName = 'aws_oidc'; + +const multiAuthDirective = + '@auth(rules: [{allow: private}, {allow: public}, {allow: private, provider: iam }, {allow: owner, provider: oidc }])'; +const ownerAuthDirective = '@auth(rules: [{allow: owner}])'; +const ownerWithIAMAuthDirective = '@auth(rules: [{allow: owner, provider: iam }])'; +const ownerRestrictedPublicAuthDirective = '@auth(rules: [{allow: owner},{allow: public, operations: [read]}])'; +const ownerRestrictedIAMPrivateAuthDirective = '@auth(rules: [{allow: owner},{allow: private, operations: [read], provider: iam }])'; +const groupsAuthDirective = '@auth(rules: [{allow: groups, groups: ["admin"] }])'; +const groupsWithApiKeyAuthDirective = '@auth(rules: [{allow: groups, groups: ["admin"]}, {allow: public, operations: [read]}])'; +const groupsWithProviderAuthDirective = '@auth(rules: [{allow: groups,groups: ["admin"], provider: iam }])'; +const ownerOpenIdAuthDirective = '@auth(rules: [{allow: owner, provider: oidc }])'; +const privateAuthDirective = '@auth(rules: [{allow: private}])'; +const publicIAMAuthDirective = '@auth(rules: [{allow: public, provider: iam }])'; +const privateWithApiKeyAuthDirective = '@auth(rules: [{allow: private, provider: apiKey }])'; +const publicAuthDirective = '@auth(rules: [{allow: public}])'; +const publicUserPoolsAuthDirective = '@auth(rules: [{allow: public, provider: userPools}])'; +const privateAndPublicDirective = '@auth(rules: [{allow: private}, {allow: public}])'; +const privateIAMDirective = '@auth(rules: [{allow: private, provider: iam}])'; +// const privateAndPrivateIAMDirective = '@auth(rules: [{allow: private}, {allow: private, provider: iam}])'; + +const getSchema = (authDirective: string) => { + return ` + type Post @model ${authDirective} { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; +}; + +const getSchemaWithFieldAuth = (authDirective: string) => { + return ` + type Post @model { + id: ID + title: String + createdAt: String + updatedAt: String + protected: String ${authDirective} + }`; +}; + +const getSchemaWithTypeAndFieldAuth = (typeAuthDirective: string, fieldAuthDirective: string) => { + return ` + type Post @model ${typeAuthDirective} { + id: ID + title: String + createdAt: String + updatedAt: String + protected: String ${fieldAuthDirective} + }`; +}; + +const getSchemaWithNonModelField = (authDirective: string) => { + return ` + type Post @model ${authDirective} { + id: ID! + title: String! + location: Location + status: Status + createdAt: String + updatedAt: String + } + + type Location { + name: String + address: Address + } + + type Address { + street: String + city: String + state: String + zip: String + } + + enum Status { + PUBLISHED, + DRAFT + }`; +}; + +const getSchemaWithRecursiveNonModelField = (authDirective: string) => { + return ` + type Post @model ${authDirective} { + id: ID! + title: String! + tags: [Tag] + } + + type Tag { + id: ID + tags: [Tag] + } + `; +}; + +const getRecursiveSchemaWithDiffModesOnParentType = (authDir1: string, authDir2: string) => { + return ` + type Post @model ${authDir1} { + id: ID! + title: String! + tags: [Tag] + } + + type Comment @model ${authDir2} { + id: ID! + content: String + tags: [Tag] + } + + type Tag { + id: ID + tags: [Tag] + } + `; +}; + +const getTransformer = (authConfig: AppSyncAuthConfiguration) => + new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new IndexTransformer(), + new HasManyTransformer(), + new HasOneTransformer(), + new BelongsToTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + +const getObjectType = (doc: DocumentNode, type: string): ObjectTypeDefinitionNode | undefined => { + return doc.definitions.find(def => def.kind === Kind.OBJECT_TYPE_DEFINITION && def.name.value === type) as + | ObjectTypeDefinitionNode + | undefined; +}; + +const expectNone = fieldOrType => { + expect(fieldOrType.directives.length === 0); +}; + +const expectOne = (fieldOrType, directiveName) => { + expect(fieldOrType.directives.length).toBe(1); + expect(fieldOrType.directives.find(d => d.name.value === directiveName)).toBeDefined(); +}; + +const expectTwo = (fieldOrType, directiveNames) => { + expect(directiveNames).toBeDefined(); + expect(directiveNames).toHaveLength(2); + expect(fieldOrType.directives.length === 2); + expect(fieldOrType.directives.find(d => d.name.value === directiveNames[0])).toBeDefined(); + expect(fieldOrType.directives.find(d => d.name.value === directiveNames[1])).toBeDefined(); +}; + +const expectMultiple = (fieldOrType: ObjectTypeDefinitionNode | FieldDefinitionNode, directiveNames: string[]) => { + expect(directiveNames).toBeDefined(); + expect(directiveNames).toHaveLength(directiveNames.length); + expect(fieldOrType.directives.length).toEqual(directiveNames.length); + directiveNames.forEach(directiveName => { + expect(fieldOrType.directives).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: expect.objectContaining({ value: directiveName }), + }), + ]), + ); + }); +}; + +const getField = (type, name) => type.fields.find(f => f.name.value === name); + +describe('validation tests', () => { + const validationTest = (authDirective, authConfig, expectedError) => { + const schema = getSchema(authDirective); + const transformer = getTransformer(authConfig); + + const t = () => { + const out = transformer.transform(schema); + }; + + expect(t).toThrowError(expectedError); + }; + + test('AMAZON_COGNITO_USER_POOLS not configured for project', () => { + validationTest( + privateAuthDirective, + apiKeyDefaultConfig, + `@auth directive with 'userPools' provider found, but the project has no Cognito User \ +Pools authentication provider configured.`, + ); + }); + + test('API_KEY not configured for project', () => { + validationTest( + publicAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'apiKey' provider found, but the project has no API Key \ +authentication provider configured.`, + ); + }); + + test('AWS_IAM not configured for project', () => { + validationTest( + publicIAMAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'iam' provider found, but the project has no IAM \ +authentication provider configured.`, + ); + }); + + test('OPENID_CONNECT not configured for project', () => { + validationTest( + ownerOpenIdAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'oidc' provider found, but the project has no OPENID_CONNECT \ +authentication provider configured.`, + ); + }); + + test(`'group' cannot have provider`, () => { + validationTest( + groupsWithProviderAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'groups' strategy only supports 'userPools' and 'oidc' providers, but found \ +'iam' assigned`, + ); + }); + + test(`'owner' has invalid IAM provider`, () => { + validationTest( + ownerWithIAMAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'owner' strategy only supports 'userPools' (default) and \ +'oidc' providers, but found 'iam' assigned.`, + ); + }); + + test(`'public' has invalid 'userPools' provider`, () => { + validationTest( + publicUserPoolsAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'public' strategy only supports 'apiKey' (default) and 'iam' providers, but \ +found 'userPools' assigned.`, + ); + }); + + test(`'private' has invalid 'apiKey' provider`, () => { + validationTest( + privateWithApiKeyAuthDirective, + userPoolsDefaultConfig, + `@auth directive with 'private' strategy only supports 'userPools' (default) and 'iam' providers, but \ +found 'apiKey' assigned.`, + ); + }); +}); + +describe('schema generation directive tests', () => { + const transformTest = (authDirective, authConfig, expectedDirectiveNames?: string[] | undefined) => { + const schema = getSchema(authDirective); + const transformer = getTransformer(authConfig); + + const out = transformer.transform(schema); + + const schemaDoc = parse(out.schema); + + const postType = getObjectType(schemaDoc, 'Post'); + + if (expectedDirectiveNames && expectedDirectiveNames.length > 0) { + let expectedDireciveNameCount = 0; + + for (const expectedDirectiveName of expectedDirectiveNames) { + expect(postType.directives.find(d => d.name.value === expectedDirectiveName)).toBeDefined(); + expectedDireciveNameCount++; + } + + expect(expectedDireciveNameCount).toEqual(postType.directives.length); + } + }; + + test(`When provider is the same as default, then no directive added`, () => { + transformTest(ownerAuthDirective, userPoolsDefaultConfig); + }); + + test(`When all providers are configured all of them are added`, () => { + const authConfig = withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS', 'AWS_IAM', 'OPENID_CONNECT']); + + (authConfig.additionalAuthenticationProviders[2] as AppSyncAuthConfigurationOIDCEntry).openIDConnectConfig = { + name: 'Test Provider', + issuerUrl: 'https://abc.def/', + }; + + transformTest(multiAuthDirective, authConfig, [userPoolsDirectiveName, iamDirectiveName, openIdDirectiveName, apiKeyDirectiveName]); + }); + + test(`Operation fields are getting the directive added, when type has the @auth for all operations`, () => { + const schema = getSchema(ownerAuthDirective); + const transformer = getTransformer(withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + const queryType = getObjectType(schemaDoc, 'Query'); + const mutationType = getObjectType(schemaDoc, 'Mutation'); + const subscriptionType = getObjectType(schemaDoc, 'Subscription'); + + const fields = [...queryType.fields, ...mutationType.fields]; + + for (const field of fields) { + expect(field.directives.length === 1); + expect(field.directives.find(d => d.name.value === userPoolsDirectiveName)).toBeDefined(); + } + + // Check that owner is required when only using owner auth rules + for (const field of subscriptionType.fields) { + expect(field.arguments).toHaveLength(1); + let arg: InputValueDefinitionNode = field.arguments[0]; + expect(arg.name.value).toEqual('owner'); + expect(arg.type.kind).toEqual(Kind.NAMED_TYPE); + } + + // Check that resolvers containing the authMode check block + const authStepSnippet = '## [Start] Authorization Steps. **'; + + expect(out.pipelineFunctions['Query.getPost.auth.1.req.vtl']).toContain(authStepSnippet); + expect(out.pipelineFunctions['Query.listPosts.auth.1.req.vtl']).toContain(authStepSnippet); + expect(out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl']).toContain(authStepSnippet); + expect(out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl']).toContain(authStepSnippet); + expect(out.pipelineFunctions['Mutation.updatePost.auth.1.res.vtl']).toContain(authStepSnippet); + expect(out.pipelineFunctions['Mutation.deletePost.auth.1.res.vtl']).toContain(authStepSnippet); + }); + + test(`Operation fields are getting the directive added, when type has the @auth only for allowed operations`, () => { + const schema = getSchema(ownerRestrictedPublicAuthDirective); + const transformer = getTransformer(withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + const queryType = getObjectType(schemaDoc, 'Query'); + const mutationType = getObjectType(schemaDoc, 'Mutation'); + const subscriptionType = getObjectType(schemaDoc, 'Subscription'); + + expectTwo(getField(queryType, 'getPost'), ['aws_cognito_user_pools', 'aws_api_key']); + expectTwo(getField(queryType, 'listPosts'), ['aws_cognito_user_pools', 'aws_api_key']); + + expectOne(getField(mutationType, 'createPost'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'updatePost'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'deletePost'), 'aws_cognito_user_pools'); + + const onCreate = getField(subscriptionType, 'onCreatePost'); + expectMultiple(onCreate, ['aws_subscribe', 'aws_api_key', 'aws_cognito_user_pools']); + expectMultiple(getField(subscriptionType, 'onUpdatePost'), ['aws_subscribe', 'aws_api_key', 'aws_cognito_user_pools']); + expectMultiple(getField(subscriptionType, 'onDeletePost'), ['aws_subscribe', 'aws_api_key', 'aws_cognito_user_pools']); + expect(onCreate.arguments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: expect.objectContaining({ value: 'owner' }), + type: expect.objectContaining({ kind: 'NamedType' }), + }), + ]), + ); + }); + + test(`Field level @auth is propagated to type and the type related operations`, () => { + const schema = getSchemaWithFieldAuth(ownerRestrictedPublicAuthDirective); + const transformer = getTransformer(withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + const queryType = getObjectType(schemaDoc, 'Query'); + const mutationType = getObjectType(schemaDoc, 'Mutation'); + + expectTwo(getField(queryType, 'getPost'), ['aws_cognito_user_pools', 'aws_api_key']); + expectTwo(getField(queryType, 'listPosts'), ['aws_cognito_user_pools', 'aws_api_key']); + + expectOne(getField(mutationType, 'createPost'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'updatePost'), 'aws_cognito_user_pools'); + // since there is only one field allowed on delete it does not have access to delete + expectNone(getField(mutationType, 'deletePost')); + + // Check that resolvers containing the authMode check block + const authModeCheckSnippet = '## [Start] Field Authorization Steps. **'; + // resolvers to check is all other resolvers other than protected + expect(out.pipelineFunctions['Post.id.req.vtl']).toContain(authModeCheckSnippet); + expect(out.pipelineFunctions['Post.title.req.vtl']).toContain(authModeCheckSnippet); + expect(out.pipelineFunctions['Post.createdAt.req.vtl']).toContain(authModeCheckSnippet); + expect(out.pipelineFunctions['Post.updatedAt.req.vtl']).toContain(authModeCheckSnippet); + }); + + test(`'groups' @auth at field level is propagated to type and the type related operations`, () => { + const schema = getSchemaWithFieldAuth(groupsAuthDirective); + const transformer = getTransformer(withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + const queryType = getObjectType(schemaDoc, 'Query'); + const mutationType = getObjectType(schemaDoc, 'Mutation'); + + expectOne(getField(queryType, 'getPost'), 'aws_cognito_user_pools'); + expectOne(getField(queryType, 'listPosts'), 'aws_cognito_user_pools'); + + expectOne(getField(mutationType, 'createPost'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'updatePost'), 'aws_cognito_user_pools'); + // since there is only one field allowed on delete it does not have access to delete + expectNone(getField(mutationType, 'deletePost')); + + // Check that resolvers containing the authMode check block + const authModeCheckSnippet = '## [Start] Field Authorization Steps. **'; + + // resolvers to check is all other resolvers other than protected + expect(out.pipelineFunctions['Post.id.req.vtl']).toContain(authModeCheckSnippet); + expect(out.pipelineFunctions['Post.title.req.vtl']).toContain(authModeCheckSnippet); + expect(out.pipelineFunctions['Post.createdAt.req.vtl']).toContain(authModeCheckSnippet); + expect(out.pipelineFunctions['Post.updatedAt.req.vtl']).toContain(authModeCheckSnippet); + }); + + test(`'groups' @auth at field level is propagated to type and the type related operations, also default provider for read`, () => { + const schema = getSchemaWithTypeAndFieldAuth(groupsAuthDirective, groupsWithApiKeyAuthDirective); + const transformer = getTransformer(withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + const queryType = getObjectType(schemaDoc, 'Query'); + const mutationType = getObjectType(schemaDoc, 'Mutation'); + + expectTwo(getField(queryType, 'getPost'), ['aws_cognito_user_pools', 'aws_api_key']); + expectTwo(getField(queryType, 'listPosts'), ['aws_cognito_user_pools', 'aws_api_key']); + + expectOne(getField(mutationType, 'createPost'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'updatePost'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'deletePost'), 'aws_cognito_user_pools'); + + // Check that resolvers containing the authMode group check + const groupCheckSnippet = '#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"admin"}] )'; + + // resolvers to check is all other resolvers other than protected by the group rule + expect(out.pipelineFunctions['Post.id.req.vtl']).toContain(groupCheckSnippet); + expect(out.pipelineFunctions['Post.title.req.vtl']).toContain(groupCheckSnippet); + expect(out.pipelineFunctions['Post.createdAt.req.vtl']).toContain(groupCheckSnippet); + expect(out.pipelineFunctions['Post.updatedAt.req.vtl']).toContain(groupCheckSnippet); + }); + + test(`Nested types without @model not getting directives applied for iam, and no policy is generated`, () => { + const schema = getSchemaWithNonModelField(''); + const transformer = getTransformer(withAuthModes(iamDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + + const locationType = getObjectType(schemaDoc, 'Location'); + const addressType = getObjectType(schemaDoc, 'Address'); + + expect(locationType.directives.length).toBe(0); + expect(addressType.directives.length).toBe(0); + + const authPolicyIdx = Object.keys(out.rootStack.Resources).find(r => r.includes('AuthRolePolicy')); + + expect(out.rootStack.Resources[authPolicyIdx]).toBeUndefined(); + }); + + test(`Nested types without @model not getting directives applied for iam, but policy is generated`, () => { + const schema = getSchemaWithNonModelField(privateIAMDirective); + const transformer = getTransformer(withAuthModes(iamDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + + const locationType = getObjectType(schemaDoc, 'Location'); + const addressType = getObjectType(schemaDoc, 'Address'); + + expect(locationType.directives.length).toBe(0); + expect(addressType.directives.length).toBe(0); + + // find the key to account for the hash + const authPolicyIdx = Object.keys(out.rootStack.Resources).find(r => r.includes('AuthRolePolicy01')); + expect(out.rootStack.Resources[authPolicyIdx]).toBeDefined; + const authRolePolicy = out.rootStack.Resources[authPolicyIdx]; + + const locationPolicy = authRolePolicy.Properties.PolicyDocument.Statement[0].Resource.filter( + r => + r['Fn::Sub'] && + r['Fn::Sub'].length && + r['Fn::Sub'].length === 2 && + r['Fn::Sub'][1].typeName && + r['Fn::Sub'][1].typeName === 'Location', + ); + expect(locationPolicy).toHaveLength(1); + + const addressPolicy = authRolePolicy.Properties.PolicyDocument.Statement[0].Resource.filter( + r => + r['Fn::Sub'] && + r['Fn::Sub'].length && + r['Fn::Sub'].length === 2 && + r['Fn::Sub'][1].typeName && + r['Fn::Sub'][1].typeName === 'Address', + ); + expect(addressPolicy).toHaveLength(1); + }); + + test(`Recursive types with diff auth modes on parent @model types`, () => { + const schema = getRecursiveSchemaWithDiffModesOnParentType(ownerAuthDirective, privateIAMDirective); + const transformer = getTransformer(withAuthModes(userPoolsDefaultConfig, ['AWS_IAM'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + + const tagType = getObjectType(schemaDoc, 'Tag'); + const expectedDirectiveNames = [userPoolsDirectiveName, iamDirectiveName]; + + expectMultiple(tagType, expectedDirectiveNames); + }); + + test(`Recursive types without @model`, () => { + const schema = getSchemaWithRecursiveNonModelField(ownerRestrictedIAMPrivateAuthDirective); + const transformer = getTransformer(withAuthModes(userPoolsDefaultConfig, ['AWS_IAM'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + + const tagType = getObjectType(schemaDoc, 'Tag'); + const expectedDirectiveNames = [userPoolsDirectiveName, iamDirectiveName]; + + expectMultiple(tagType, expectedDirectiveNames); + }); + + test(`Nested types without @model getting directives applied (cognito default, api key additional)`, () => { + const schema = getSchemaWithNonModelField(privateAndPublicDirective); + const transformer = getTransformer(withAuthModes(userPoolsDefaultConfig, ['API_KEY'])); + + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + + const locationType = getObjectType(schemaDoc, 'Location'); + const addressType = getObjectType(schemaDoc, 'Address'); + const expectedDirectiveNames = [userPoolsDirectiveName, apiKeyDirectiveName]; + + if (expectedDirectiveNames && expectedDirectiveNames.length > 0) { + let expectedDireciveNameCount = 0; + + for (const expectedDirectiveName of expectedDirectiveNames) { + expect(locationType.directives.find(d => d.name.value === expectedDirectiveName)).toBeDefined(); + expectedDireciveNameCount++; + } + + expect(expectedDireciveNameCount).toEqual(locationType.directives.length); + + expectedDireciveNameCount = 0; + + for (const expectedDirectiveName of expectedDirectiveNames) { + expect(addressType.directives.find(d => d.name.value === expectedDirectiveName)).toBeDefined(); + expectedDireciveNameCount++; + } + + expect(expectedDireciveNameCount).toEqual(addressType.directives.length); + } + }); + + test(`ModelXConnection type is getting the directives added, when a field has @hasMany but one fo the types has no queries defined`, () => { + const validSchema = ` + type User @model + @auth(rules: [ + { allow: private, provider: iam, operations: [read] } + { allow: groups, groups: ["group"], operations: [read, update, delete] }, + ]) { + id: ID! + posts: [Post!] @hasMany(indexName: "byUser", fields: ["id"]) + } + type Post @model(queries: null) + @auth(rules: [ + { allow: private, provider: iam, operations: [read] }, + { allow: groups, groups: ["group"], operations: [read, update, delete] } + ]) { + id: ID! + postUserId: ID! @index(name: "byUser") + message: String + }`; + const transformer = getTransformer(withAuthModes(iamDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + const out = transformer.transform(validSchema); + const schemaDoc = parse(out.schema); + const queryType = getObjectType(schemaDoc, 'Query'); + const mutationType = getObjectType(schemaDoc, 'Mutation'); + + expectTwo(getField(queryType, 'getUser'), ['aws_iam', 'aws_cognito_user_pools']); + expectTwo(getField(queryType, 'listUsers'), ['aws_iam', 'aws_cognito_user_pools']); + + expectNone(getField(mutationType, 'createUser')); + expectOne(getField(mutationType, 'updateUser'), 'aws_cognito_user_pools'); + expectOne(getField(mutationType, 'deleteUser'), 'aws_cognito_user_pools'); + + const userType = getObjectType(schemaDoc, 'User'); + expectTwo(userType, ['aws_iam', 'aws_cognito_user_pools']); + expectNone(getField(userType, 'posts')); + + const modelPostConnectionType = getObjectType(schemaDoc, 'ModelPostConnection'); + expect(modelPostConnectionType).toBeDefined(); + expectTwo(modelPostConnectionType, ['aws_iam', 'aws_cognito_user_pools']); + }); + + test(`ModelXConnection type is getting the directives added, when a field has @connection but one of the types has no queries defined. Many to Many`, () => { + const schema = ` + type Post @model @auth(rules: [{ allow: owner }]) { + id: ID! + title: String! + editors: [PostEditor] @hasMany(indexName: "byPost", fields: ["id"]) + } + # Create a join model and disable queries as you don't need them + # and can query through Post.editors and User.posts + type PostEditor + @model(queries: null) + @auth(rules: [{ allow: owner }]) { + id: ID! + postID: ID! @index(name: "byPost", sortKeyFields: ["editorID"]) + editorID: ID! @index(name: "byEditor", sortKeyFields: ["postID"]) + post: Post! @belongsTo(fields: ["postID"]) + editor: User! @belongsTo(fields: ["editorID"]) + } + type User @model @auth(rules: [{ allow: owner }]) { + id: ID! + username: String! + posts: [PostEditor] @hasMany(indexName: "byEditor", fields: ["id"]) + }`; + + const transformer = getTransformer(withAuthModes(apiKeyDefaultConfig, ['AMAZON_COGNITO_USER_POOLS'])); + const out = transformer.transform(schema); + const schemaDoc = parse(out.schema); + + const modelPostEditorConnectionType = getObjectType(schemaDoc, 'ModelPostEditorConnection'); + expect(modelPostEditorConnectionType).toBeDefined(); + // since we have resolver level auth to deny providers the default is added here to ensure the access is granted if the default type is not applied on the parent + // therefore we just need to make sure that the access is at least granted on the schema level + expect(modelPostEditorConnectionType.directives.some(dir => dir.name.value === 'aws_cognito_user_pools')).toBe(true); + }); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/owner-auth.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/owner-auth.test.ts new file mode 100644 index 00000000000..db5c231707c --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/owner-auth.test.ts @@ -0,0 +1,230 @@ +import { parse } from 'graphql'; +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { ResourceConstants } from 'graphql-transformer-common'; +import { getField, getObjectType } from './test-helpers'; +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; + +test('auth transformer validation happy case', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [{allow: owner}]) { + id: ID! + title: String! + createdAt: String + updatedAt: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.rootStack.Resources[ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual( + 'AMAZON_COGNITO_USER_POOLS', + ); +}); + +test('ownerfield with subscriptions', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const validSchema = ` + type Post @model @auth(rules: [ + {allow: owner, ownerField: "postOwner"} + ]){ + id: ID! + title: String + postOwner: String + }`; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + + // expect 'postOwner' as an argument for subscription operations + expect(out.schema).toContain('onCreatePost(postOwner: String)'); + expect(out.schema).toContain('onUpdatePost(postOwner: String)'); + expect(out.schema).toContain('onDeletePost(postOwner: String)'); + + // expect logic in the resolvers to check for postOwner args as an allowed owner + expect(out.pipelineFunctions['Subscription.onCreatePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.postOwner, null) )', + ); + expect(out.pipelineFunctions['Subscription.onUpdatePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.postOwner, null) )', + ); + expect(out.pipelineFunctions['Subscription.onDeletePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.postOwner, null) )', + ); +}); + +test('multiple owner rules with subscriptions', () => { + const validSchema = ` + type Post @model + @auth(rules: [ + { allow: owner }, + { allow: owner, ownerField: "editor", operations: [read, update] } + ]) + { + id: ID! + title: String + owner: String + editor: String + }`; + + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + + // expect 'owner' and 'editors' as arguments for subscription operations + expect(out.schema).toContain('onCreatePost(owner: String, editor: String)'); + expect(out.schema).toContain('onUpdatePost(owner: String, editor: String)'); + expect(out.schema).toContain('onDeletePost(owner: String, editor: String)'); + + // expect logic in the resolvers to check for owner args as an allowedOwner + expect(out.pipelineFunctions['Subscription.onCreatePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.owner, null) )', + ); + expect(out.pipelineFunctions['Subscription.onUpdatePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.owner, null) )', + ); + expect(out.pipelineFunctions['Subscription.onDeletePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.owner, null) )', + ); + + // expect logic in the resolvers to check for editor args as an allowedOwner + expect(out.pipelineFunctions['Subscription.onCreatePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity1 = $util.defaultIfNull($ctx.args.editor, null) )', + ); + expect(out.pipelineFunctions['Subscription.onUpdatePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity1 = $util.defaultIfNull($ctx.args.editor, null) )', + ); + expect(out.pipelineFunctions['Subscription.onDeletePost.auth.1.req.vtl']).toContain( + '#set( $ownerEntity1 = $util.defaultIfNull($ctx.args.editor, null) )', + ); +}); + +test('implicit owner fields get added to the type', () => { + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const validSchema = ` + type Post @model + @auth(rules: [ + {allow: owner, ownerField: "postOwner"} + { allow: owner, ownerField: "customOwner", identityClaim: "sub"} + ]) + { + id: ID! + title: String + } + `; + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + + const schema = parse(out.schema); + const postType = getObjectType(schema, 'Post'); + expect(postType).toBeDefined(); + + const postOwnerField = getField(postType, 'postOwner'); + expect(postOwnerField).toBeDefined(); + expect((postOwnerField as any).type.name.value).toEqual('String'); + + const customOwner = getField(postType, 'customOwner'); + expect(customOwner).toBeDefined(); + expect((customOwner as any).type.name.value).toEqual('String'); +}); + +test('implicit owner fields from field level auth get added to the type', () => { + const validSchema = ` + type Post @model + { + id: ID + title: String + protectedField: String @auth(rules: [ + {allow: owner, ownerField: "postOwner"} + { allow: owner, ownerField: "customOwner", identityClaim: "sub"} + ]) + }`; + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + const schema = parse(out.schema); + const postType = getObjectType(schema, 'Post'); + expect(postType).toBeDefined(); + + const postOwnerField = getField(postType, 'postOwner'); + expect(postOwnerField).toBeDefined(); + expect((postOwnerField as any).type.name.value).toEqual('String'); + + const customOwner = getField(postType, 'customOwner'); + expect(customOwner).toBeDefined(); + expect((customOwner as any).type.name.value).toEqual('String'); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/searchable-auth.test.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/searchable-auth.test.ts new file mode 100644 index 00000000000..ee6677bcf10 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/searchable-auth.test.ts @@ -0,0 +1,93 @@ +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { SearchableModelTransformer } from '@aws-amplify/graphql-searchable-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; + +test('auth logic is enabled on owner/static rules in es request', () => { + const validSchema = ` + type Comment @model + @searchable + @auth(rules: [ + { allow: owner } + { allow: groups, groups: ["writer"]} + ]) + { + id: ID! + content: String + } + `; + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new SearchableModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + // expect response resolver to contain auth logic for owner rule + expect(out).toBeDefined(); + expect(out.pipelineFunctions['Query.searchComments.auth.1.req.vtl']).toContain( + '"terms": [$util.defaultIfNull($ctx.identity.claims.get("username"), $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), "___xamznone____"))],', + ); + // expect response resolver to contain auth logic for group rule + expect(out.pipelineFunctions['Query.searchComments.auth.1.req.vtl']).toContain( + '#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"writer"}] )', + ); +}); + +test('auth logic is enabled for iam/apiKey auth rules', () => { + const validSchema = ` + type Post @model + @searchable + @auth(rules: [ + { allow: public, provider: apiKey } # api key is allowed + { allow: private, provider: iam } # auth roles are allowed + ]) { + id: ID! + content: String + secret: String @auth(rules: [{ allow: private, provider: iam }]) # only auth role can do crud on this + } + `; + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'API_KEY', + apiKeyConfig: { + description: 'E2E Test API Key', + apiKeyExpirationDays: 300, + }, + }, + { + authenticationType: 'AWS_IAM', + }, + ], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new SearchableModelTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const out = transformer.transform(validSchema); + expect(out).toBeDefined(); + expect(out.schema).toContain('SearchablePostConnection @aws_api_key @aws_iam'); +}); diff --git a/packages/amplify-graphql-auth-transformer/src/__tests__/test-helpers.ts b/packages/amplify-graphql-auth-transformer/src/__tests__/test-helpers.ts new file mode 100644 index 00000000000..7b92473311a --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/__tests__/test-helpers.ts @@ -0,0 +1,10 @@ +import { ObjectTypeDefinitionNode, FieldDefinitionNode, DocumentNode, Kind } from 'graphql'; + +export const getObjectType = (doc: DocumentNode, type: string): ObjectTypeDefinitionNode | undefined => { + return doc.definitions.find(def => def.kind === Kind.OBJECT_TYPE_DEFINITION && def.name.value === type) as + | ObjectTypeDefinitionNode + | undefined; +}; +export const getField = (obj: ObjectTypeDefinitionNode, fieldName: string): FieldDefinitionNode | void => { + return obj.fields?.find(f => f.name.value === fieldName); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/accesscontrol/acm.ts b/packages/amplify-graphql-auth-transformer/src/accesscontrol/acm.ts new file mode 100644 index 00000000000..77166997285 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/accesscontrol/acm.ts @@ -0,0 +1,193 @@ +import assert from 'assert'; + +type ACMConfig = { + resources: string[]; + operations: string[]; +}; + +type SetRoleInput = { + role: string; + operations: Array; + resource?: string; +}; + +type ValidateInput = { + role?: string; + resource?: string; + operations?: Array; +}; + +type ResourceOperationInput = { + operations: Array; + role?: string; + resource?: string; +}; + +/** + * Creates an access control matrix + * The following vectors are used + * - Roles + * - Resources + * - Operations + */ +export class AccessControlMatrix { + private roles: Array; + private operations: Array; + private resources: Array; + private matrix: Array>>; + + constructor(config: ACMConfig) { + this.operations = config.operations; + this.resources = config.resources; + this.matrix = new Array(); + this.roles = new Array(); + } + + public setRole(input: SetRoleInput): void { + const { role, resource, operations } = input; + this.validate({ resource, operations }); + let allowedVector: Array>; + if (!this.roles.includes(role)) { + allowedVector = this.getResourceOperationMatrix({ operations, resource }); + this.roles.push(input.role); + this.matrix.push(allowedVector); + assert(this.roles.length === this.matrix.length, 'Roles are not aligned with Roles added in Matrix'); + } else { + allowedVector = this.getResourceOperationMatrix({ operations, resource, role }); + const roleIndex = this.roles.indexOf(role); + this.matrix[roleIndex] = allowedVector; + } + } + + public hasRole(role: string): boolean { + return this.roles.includes(role); + } + + public getRoles(): Array { + return this.roles; + } + + public getResources(): Readonly> { + return this.resources; + } + + public isAllowed(role: string, resource: string, operation: string): boolean { + this.validate({ role, resource, operations: [operation] }); + const roleIndex = this.roles.indexOf(role); + const resourceIndex = this.resources.indexOf(resource); + const operationIndex = this.operations.indexOf(operation); + return this.matrix[roleIndex][resourceIndex][operationIndex]; + } + + public resetAccessForResource(resource: string): void { + this.validate({ resource }); + const resourceIndex = this.resources.indexOf(resource); + for (let i = 0; i < this.roles.length; i++) { + this.matrix[i][resourceIndex] = new Array(this.operations.length).fill(false); + } + } + + /** + * Given an operation returns the roles which have access to at least one resource + * If fullAccess is true then it returns roles which have operation access on all resources + * @param operation string + * @param fullAccess boolean + * @returns array of roles + */ + public getRolesPerOperation(operation: string, fullAccess: boolean = false): Array { + this.validate({ operations: [operation] }); + const operationIndex = this.operations.indexOf(operation); + const roles = new Array(); + for (let x = 0; x < this.roles.length; x++) { + let hasOperation: boolean = false; + if (fullAccess) { + hasOperation = this.resources.every((resource, idx) => { + return this.matrix[x][idx][operationIndex]; + }); + } else { + hasOperation = this.resources.some((resource, idx) => { + return this.matrix[x][idx][operationIndex]; + }); + } + if (hasOperation) roles.push(this.roles[x]); + } + return roles; + } + + /** + * @returns a map of role and their access + * this object can then be used in console.table() + */ + public getAcmPerRole(): Map { + const acmPerRole: Map = new Map(); + for (let i = 0; i < this.matrix.length; i++) { + let tableObj: any = {}; + for (let y = 0; y < this.matrix[i].length; y++) { + tableObj[this.resources[y]] = this.matrix[i][y].reduce((prev: any, resource: boolean, index: number) => { + prev[this.operations[index]] = resource; + return prev; + }, {}); + } + acmPerRole.set(this.roles[i], tableObj); + } + return acmPerRole; + } + + /** + * helpers + */ + private validate(input: ValidateInput) { + if (input.resource && !this.resources.includes(input.resource)) { + throw Error(`Resource: ${input.resource} is not configued in the ACM`); + } + if (input.role && !this.roles.includes(input.role)) { + throw Error(`Role: ${input.role} does not exist in ACM.`); + } + if (input.operations) { + input.operations.forEach(operation => { + if (this.operations.indexOf(operation) === -1) throw Error(`Operation: ${operation} does not exist in the ACM.`); + }); + } + } + + /** + * + * if singular resource is specified the operations are only applied on the resource + * a denied array for every other resource is returned in the matrix + * @param operations + * @param resource + * @returns a 2d matrix containg the access for each resource + */ + private getResourceOperationMatrix(input: ResourceOperationInput): Array> { + const { operations, resource, role } = input; + let fieldAllowVector: boolean[][] = []; + let operationList: boolean[] = this.getOperationList(operations); + if (role && resource) { + const roleIndex = this.roles.indexOf(role); + const resourceIndex = this.resources.indexOf(resource); + fieldAllowVector = this.matrix[roleIndex]; + fieldAllowVector[resourceIndex] = operationList; + return fieldAllowVector; + } + for (let i = 0; i < this.resources.length; i++) { + if (resource) { + if (this.resources.indexOf(resource) === i) { + fieldAllowVector.push(operationList); + } else { + fieldAllowVector.push(new Array(this.operations.length).fill(false)); + } + } else { + fieldAllowVector.push(operationList); + } + } + return fieldAllowVector; + } + + private getOperationList(operations: Array): Array { + let operationList: Array = new Array(); + for (let operation of this.operations) { + operationList.push(operations.includes(operation)); + } + return operationList; + } +} diff --git a/packages/amplify-graphql-auth-transformer/src/accesscontrol/index.ts b/packages/amplify-graphql-auth-transformer/src/accesscontrol/index.ts new file mode 100644 index 00000000000..e07fbd95541 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/accesscontrol/index.ts @@ -0,0 +1 @@ +export { AccessControlMatrix } from './acm'; diff --git a/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts b/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts new file mode 100644 index 00000000000..4d83bdaf2d3 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts @@ -0,0 +1,952 @@ +import { + DirectiveWrapper, + TransformerContractError, + TransformerAuthBase, + InvalidDirectiveError, + MappingTemplate, + IAM_AUTH_ROLE_PARAMETER, + IAM_UNAUTH_ROLE_PARAMETER, + TransformerResolver, +} from '@aws-amplify/graphql-transformer-core'; +import { + DataSourceProvider, + MutationFieldType, + QueryFieldType, + TransformerTransformSchemaStepContextProvider, + TransformerContextProvider, + TransformerResolverProvider, + TransformerSchemaVisitStepContextProvider, + TransformerAuthProvider, + TransformerBeforeStepContextProvider, +} from '@aws-amplify/graphql-transformer-interfaces'; +import { + AuthRule, + authDirectiveDefinition, + ConfiguredAuthProviders, + getConfiguredAuthProviders, + AuthTransformerConfig, + collectFieldNames, + DEFAULT_GROUP_CLAIM, + MODEL_OPERATIONS, + ModelOperation, + ensureAuthRuleDefaults, + DEFAULT_IDENTITY_CLAIM, + DEFAULT_GROUPS_FIELD, + DEFAULT_OWNER_FIELD, + getModelConfig, + validateFieldRules, + validateRules, + AuthProvider, + AUTH_PROVIDER_DIRECTIVE_MAP, + extendTypeWithDirectives, + RoleDefinition, + addDirectivesToOperation, + createPolicyDocumentForManagedPolicy, + getQueryFieldNames, + getMutationFieldNames, + addSubscriptionArguments, + fieldIsList, + getSubscriptionFieldNames, + addDirectivesToField, + getSearchableConfig, + getStackForField, + NONE_DS, +} from './utils'; +import { + DirectiveNode, + FieldDefinitionNode, + ObjectTypeDefinitionNode, + InterfaceTypeDefinitionNode, + Kind, + TypeDefinitionNode, +} from 'graphql'; +import { SubscriptionLevel, ModelDirectiveConfiguration } from '@aws-amplify/graphql-model-transformer'; +import { AccessControlMatrix } from './accesscontrol'; +import { getBaseType, makeDirective, makeField, makeNamedType, ResourceConstants } from 'graphql-transformer-common'; +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import { + generateAuthExpressionForCreate, + generateAuthExpressionForUpdate, + generateAuthRequestExpression, + geneateAuthExpressionForDelete, + generateAuthExpressionForField, + generateFieldAuthResponse, + generateAuthExpressionForQueries, + generateAuthExpressionForSubscriptions, +} from './resolvers'; +import { toUpper } from 'graphql-transformer-common'; + +// @ auth +// changing the schema +// - transformSchema +// adding resolver +// - generateResolver +// editing IAM policy +// - generateResolver (cdk) +// resolver mapping + +// resolver.ts for auth pipeline slots + +export class AuthTransformer extends TransformerAuthBase implements TransformerAuthProvider { + private config: AuthTransformerConfig; + private configuredAuthProviders: ConfiguredAuthProviders; + // access control + private roleMap: Map; + private authModelConfig: Map; + private authNonModelConfig: Map; + // model config + private modelDirectiveConfig: Map; + // schema generation + private seenNonModelTypes: Map>; + // iam policy generation + private generateIAMPolicyforUnauthRole: boolean; + private generateIAMPolicyforAuthRole: boolean; + private authPolicyResources = new Set(); + private unauthPolicyResources = new Set(); + + constructor(config: AuthTransformerConfig) { + super('amplify-auth-transformer', authDirectiveDefinition); + this.config = config; + this.modelDirectiveConfig = new Map(); + this.seenNonModelTypes = new Map(); + this.authModelConfig = new Map(); + this.roleMap = new Map(); + this.generateIAMPolicyforUnauthRole = false; + this.generateIAMPolicyforAuthRole = false; + this.authNonModelConfig = new Map(); + } + + before = (context: TransformerBeforeStepContextProvider): void => { + // if there was no auth config in the props we add the authConfig from the context + this.config.authConfig = this.config.authConfig || context.authConfig; + this.configuredAuthProviders = getConfiguredAuthProviders(this.config); + }; + + object = (def: ObjectTypeDefinitionNode, directive: DirectiveNode, context: TransformerSchemaVisitStepContextProvider): void => { + const modelDirective = def.directives?.find(dir => dir.name.value === 'model'); + if (!modelDirective) { + throw new TransformerContractError('Types annotated with @auth must also be annotated with @model.'); + } + const typeName = def.name.value; + const authDir = new DirectiveWrapper(directive); + const rules: AuthRule[] = authDir.getArguments<{ rules: Array }>({ rules: [] }).rules; + ensureAuthRuleDefaults(rules); + // validate rules + validateRules(rules, this.configuredAuthProviders); + // create access control for object + const acm = new AccessControlMatrix({ + operations: MODEL_OPERATIONS, + resources: collectFieldNames(def), + }); + // Check the rules to see if we should generate Auth/Unauth Policies + this.setAuthPolicyFlag(rules); + this.setUnauthPolicyFlag(rules); + // add object into policy + this.addTypeToResourceReferences(def.name.value, rules); + // turn rules into roles and add into acm and roleMap + this.convertRulesToRoles(acm, rules); + this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName, context.isProjectUsingDataStore())); + this.authModelConfig.set(typeName, acm); + }; + + field = ( + parent: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, + field: FieldDefinitionNode, + directive: DirectiveNode, + context: TransformerSchemaVisitStepContextProvider, + ): void => { + if (parent.kind === Kind.INTERFACE_TYPE_DEFINITION) { + throw new InvalidDirectiveError( + `The @auth directive cannot be placed on an interface's field. See ${parent.name.value}${field.name.value}`, + ); + } + const isParentTypeBuiltinType = + parent.name.value === context.output.getQueryTypeName() || + parent.name.value === context.output.getMutationTypeName() || + parent.name.value === context.output.getSubscriptionTypeName(); + + if (isParentTypeBuiltinType) { + console.warn( + `Be careful when using @auth directives on a field in a root type. @auth directives on field definitions use the source \ +object to perform authorization logic and the source will be an empty object for fields on root types. \ +Static group authorization should perform as expected.`, + ); + } + // context.api.host.resolver + // context.resolver -> resolver manager -> dynamodb, relation directives, searchable + // creates field resolver + + const modelDirective = parent.directives?.find(dir => dir.name.value === 'model'); + const typeName = parent.name.value; + const fieldName = field.name.value; + const authDir = new DirectiveWrapper(directive); + const rules: AuthRule[] = authDir.getArguments<{ rules: Array }>({ rules: [] }).rules; + ensureAuthRuleDefaults(rules); + validateFieldRules(rules, isParentTypeBuiltinType, modelDirective !== undefined, this.configuredAuthProviders); + + // regardless if a model directive is used we generate the policy for iam auth + this.setAuthPolicyFlag(rules); + this.setUnauthPolicyFlag(rules); + this.addFieldToResourceReferences(parent.name.value, field.name.value, rules); + + if (modelDirective) { + // auth on models + let acm: AccessControlMatrix; + // check if the parent is already in the model config if not add it + if (!this.modelDirectiveConfig.has(typeName)) { + this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName, context.isProjectUsingDataStore())); + acm = new AccessControlMatrix({ + operations: MODEL_OPERATIONS, + resources: collectFieldNames(parent), + }); + } else { + acm = this.authModelConfig.get(typeName) as AccessControlMatrix; + acm.resetAccessForResource(fieldName); + } + this.convertRulesToRoles(acm, rules, fieldName); + this.authModelConfig.set(typeName, acm); + } else { + // if @auth is used without @model only generate static group rules in the resolver + // since we only protect the field for non models we store the typeName + fieldName + // in the authNonModelTypes map + const staticRules = rules.filter((rule: AuthRule) => rule.allow !== 'owner' && !rule.groupsField); + const typeFieldName = `${typeName}:${fieldName}`; + const acm = new AccessControlMatrix({ + operations: ['read'], + resources: [typeFieldName], + }); + this.convertRulesToRoles(acm, staticRules, typeFieldName, ['read']); + this.authNonModelConfig.set(typeFieldName, acm); + } + }; + + transformSchema = (context: TransformerTransformSchemaStepContextProvider): void => { + const getOwnerFields = (acm: AccessControlMatrix) => { + return acm.getRoles().reduce((prev: string[], role: string) => { + if (this.roleMap.get(role)!.strategy === 'owner') prev.push(this.roleMap.get(role)!.entity!); + return prev; + }, []); + }; + for (let [modelName, acm] of this.authModelConfig) { + const def = context.output.getObject(modelName)!; + // collect ownerFields and them in the model + this.addFieldsToObject(context, modelName, getOwnerFields(acm)); + // Get the directives we need to add to the GraphQL nodes + const providers = this.getAuthProviders(acm.getRoles()); + const directives = this.getServiceDirectives(providers, providers.length === 0 ? this.shouldAddDefaultServiceDirective() : false); + if (directives.length > 0) { + extendTypeWithDirectives(context, modelName, directives); + } + this.protectSchemaOperations(context, def, acm); + this.propagateAuthDirectivesToNestedTypes(context, context.output.getObject(modelName)!, providers); + } + for (let [typeFieldName, acm] of this.authNonModelConfig) { + // protect the non model field + const [typeName, fieldName] = typeFieldName.split(':'); + const providers = this.getAuthProviders(acm.getRoles()); + const directives = this.getServiceDirectives(providers, false); + if (directives.length > 0) { + addDirectivesToField(context, typeName, fieldName, directives); + } + } + }; + + generateResolvers = (context: TransformerContextProvider): void => { + // generate iam policies + this.generateIAMPolicies(context); + // generate auth resolver code + for (let [modelName, acm] of this.authModelConfig) { + const indexKeyName = `${modelName}:indicies`; + const def = context.output.getObject(modelName)!; + const searchableDirective = def.directives.find(dir => dir.name.value === 'searchable'); + // queries + const queryFields = getQueryFieldNames(this.modelDirectiveConfig.get(modelName)!); + for (let query of queryFields.values()) { + switch (query.type) { + case QueryFieldType.GET: + this.protectGetResolver(context, def, query.typeName, query.fieldName, acm); + break; + case QueryFieldType.LIST: + this.protectListResolver(context, def, query.typeName, query.fieldName, acm); + break; + case QueryFieldType.SYNC: + this.protectSyncResolver(context, def, query.typeName, query.fieldName, acm); + break; + default: + throw new TransformerContractError('Unkown query field type'); + } + } + // protect additional query fields if they exist + if (context.metadata.has(indexKeyName)) { + for (let index of context.metadata.get>(indexKeyName)!.values()) { + this.protectListResolver(context, def, def.name.value, index, acm); + } + } + // check if searchable if included in the typeName + if (searchableDirective) { + // protect search query + const config = getSearchableConfig(searchableDirective, modelName); + this.protectSearchResolver(context, def, context.output.getQueryTypeName()!, config.queries.search, acm); + } + // get fields specified in the schema + // if there is a role that does not have read access on the field then we create a field resolver + const readRoles = acm.getRolesPerOperation('read'); + const modelFields = def.fields?.filter(f => acm.getResources().includes(f.name.value)) ?? []; + for (let field of modelFields) { + const allowedRoles = readRoles.filter(r => acm.isAllowed(r, field.name.value, 'read')); + if (allowedRoles.length < readRoles.length) { + if (field.type.kind === Kind.NON_NULL_TYPE) { + throw new InvalidDirectiveError(`\nPer-field auth on the required field ${field.name.value} is not supported with subscriptions. + Either make the field optional, set auth on the object and not the field, or disable subscriptions for the object (setting level to off or public)\n`); + } + this.protectFieldResolver(context, def, modelName, field.name.value, allowedRoles); + } + } + const mutationFields = getMutationFieldNames(this.modelDirectiveConfig.get(modelName)!); + for (let mutation of mutationFields.values()) { + switch (mutation.type) { + case MutationFieldType.CREATE: + this.protectCreateResolver(context, def, mutation.typeName, mutation.fieldName, acm); + break; + case MutationFieldType.UPDATE: + this.protectUpdateResolver(context, def, mutation.typeName, mutation.fieldName, acm); + break; + case MutationFieldType.DELETE: + this.protectDeleteResolver(context, def, mutation.typeName, mutation.fieldName, acm); + break; + default: + throw new TransformerContractError('Unkown Mutation field type'); + } + } + + const subscriptionFieldNames = getSubscriptionFieldNames(this.modelDirectiveConfig.get(modelName)!); + const subscriptionRoles = acm + .getRolesPerOperation('read') + .map(role => this.roleMap.get(role)!) + // for subscriptions we only use static rules or owner rule where the field is not a list + .filter(roleDef => (roleDef.strategy === 'owner' && !fieldIsList(def.fields ?? [], roleDef.entity!)) || roleDef.static); + for (let subscription of subscriptionFieldNames) { + this.protectSubscriptionResolver(context, subscription.typeName, subscription.fieldName, subscriptionRoles); + } + } + + for (let [typeFieldName, acm] of this.authNonModelConfig) { + // field resolvers + const [typeName, fieldName] = typeFieldName.split(':'); + const def = context.output.getObject(typeName); + this.protectFieldResolver(context, def, typeName, fieldName, acm.getRoles()); + } + }; + + protectSchemaOperations = ( + ctx: TransformerTransformSchemaStepContextProvider, + def: ObjectTypeDefinitionNode, + acm: AccessControlMatrix, + ): void => { + const modelConfig = this.modelDirectiveConfig.get(def.name.value)!; + const indexKeyName = `${def.name.value}:indicies`; + const searchableDirective = def.directives.find(dir => dir.name.value === 'searchable'); + const addServiceDirective = (typeName: string, operation: ModelOperation, operationName: string | null = null) => { + if (operationName) { + const includeDefault = this.doesTypeHaveRulesForOperation(acm, operation); + const providers = this.getAuthProviders(acm.getRolesPerOperation(operation, operation === 'delete')); + const operationDirectives = this.getServiceDirectives(providers, includeDefault); + if (operationDirectives.length > 0) { + addDirectivesToOperation(ctx, typeName, operationName, operationDirectives); + } + this.addOperationToResourceReferences(typeName, operationName, acm.getRoles()); + } + }; + // default model operations TODO: protect sync queries once supported + addServiceDirective(ctx.output.getQueryTypeName()!, 'read', modelConfig?.queries?.get); + addServiceDirective(ctx.output.getQueryTypeName()!, 'read', modelConfig?.queries?.list); + addServiceDirective(ctx.output.getMutationTypeName()!, 'create', modelConfig?.mutations?.create); + addServiceDirective(ctx.output.getMutationTypeName()!, 'update', modelConfig?.mutations?.update); + addServiceDirective(ctx.output.getMutationTypeName()!, 'delete', modelConfig?.mutations?.delete); + // @index queries + if (ctx.metadata.has(indexKeyName)) { + for (let index of ctx.metadata.get>(indexKeyName)!.values()) { + addServiceDirective(ctx.output.getQueryTypeName(), 'read', index); + } + } + // @searchable + if (searchableDirective) { + const config = getSearchableConfig(searchableDirective, def.name.value); + addServiceDirective(ctx.output.getQueryTypeName(), 'read', config.queries.search); + } + + const subscriptions = modelConfig?.subscriptions; + if (subscriptions.level === SubscriptionLevel.on) { + const subscriptionArguments = acm + .getRolesPerOperation('read') + .map(role => this.roleMap.get(role)!) + .filter(roleDef => roleDef.strategy === 'owner' && !fieldIsList(def.fields ?? [], roleDef.entity!)); + if (subscriptions.onCreate && modelConfig?.mutations?.create) { + for (let onCreateSub of subscriptions.onCreate) { + addServiceDirective(ctx.output.getSubscriptionTypeName()!, 'read', onCreateSub); + addSubscriptionArguments(ctx, onCreateSub, subscriptionArguments); + } + } + if (subscriptions.onUpdate && modelConfig?.mutations?.update) { + for (let onUpdateSub of subscriptions.onUpdate) { + addServiceDirective(ctx.output.getSubscriptionTypeName()!, 'read', onUpdateSub); + addSubscriptionArguments(ctx, onUpdateSub, subscriptionArguments); + } + } + if (subscriptions.onDelete && modelConfig?.mutations?.delete) { + for (let onDeleteSub of subscriptions.onDelete) { + addServiceDirective(ctx.output.getSubscriptionTypeName()!, 'read', onDeleteSub); + addSubscriptionArguments(ctx, onDeleteSub, subscriptionArguments); + } + } + } + }; + + protectGetResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const roleDefinitions = acm.getRolesPerOperation('read').map(r => this.roleMap.get(r)!); + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + }; + protectListResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const roleDefinitions = acm.getRolesPerOperation('read').map(r => this.roleMap.get(r)!); + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + }; + protectSyncResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + if (ctx.isProjectUsingDataStore()) { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const roleDefinitions = acm.getRolesPerOperation('read').map(r => this.roleMap.get(r)!); + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + } + }; + protectSearchResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const roleDefinitions = acm.getRolesPerOperation('read').map(r => this.roleMap.get(r)!); + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? [], 'opensearch'); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + }; + /* + Field Resovler can protect the following + - model fields + - relational fields (hasOne, hasMany, belongsTo) + - fields on an operation (query/mutation) + - protection on predictions/function/no directive + Order of precendence + - resolver in api host (ex. @function, @predictions) + - resolver in resolver manager (ex. @hasOne, @hasMany @belongsTo) + - no resolver creates a blank non-pipeline resolver will return the source field + */ + protectFieldResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + roles: Array, + ): void => { + const roleDefinitions = roles.map(r => this.roleMap.get(r)!); + const hasModelDirective = def.directives.some(dir => dir.name.value === 'model'); + const stack = getStackForField(ctx, def, fieldName, hasModelDirective); + if (ctx.api.host.hasResolver(typeName, fieldName)) { + // TODO: move pipeline resolvers created in the api host to the resolver manager + const fieldResolver = ctx.api.host.getResolver(typeName, fieldName) as any; + const fieldAuthExpression = generateAuthExpressionForField(this.configuredAuthProviders, roleDefinitions, []); + if (!ctx.api.host.hasDataSource(NONE_DS)) { + ctx.api.host.addNoneDataSource(NONE_DS); + } + const authFunction = ctx.api.host.addAppSyncFunction( + `${toUpper(typeName)}${toUpper(fieldName)}AuthFN`, + MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.auth.req.vtl`), + MappingTemplate.inlineTemplateFromString('$util.toJson({})'), + NONE_DS, + stack, + ); + (fieldResolver.pipelineConfig.functions as string[]).unshift(authFunction.functionId); + } else if (ctx.resolvers.hasResolver(typeName, fieldName)) { + // if there a resolver in the resolver manager we can append to the auth slot + const fieldResolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); + fieldResolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + } else { + const fieldAuthExpression = generateAuthExpressionForField(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); + const subsEnabled = hasModelDirective ? this.modelDirectiveConfig.get(typeName)!.subscriptions.level === 'on' : false; + const fieldResponse = generateFieldAuthResponse('Mutation', fieldName, subsEnabled); + const resolver = ctx.resolvers.addResolver( + typeName, + fieldName, + new TransformerResolver( + typeName, + fieldName, + MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.req.vtl`), + MappingTemplate.s3MappingTemplateFromString(fieldResponse, `${typeName}.${fieldName}.res.vtl`), + ['init'], + ['finish'], + ), + ); + resolver.mapToStack(stack); + } + }; + protectCreateResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const fields = acm.getResources(); + const createRoles = acm.getRolesPerOperation('create').map(role => { + const allowedFields = fields.filter(resource => acm.isAllowed(role, resource, 'create')); + const roleDefinition = this.roleMap.get(role)!; + roleDefinition.allowedFields = allowedFields.length === fields.length ? [] : allowedFields; + return roleDefinition; + }); + const authExpression = generateAuthExpressionForCreate(this.configuredAuthProviders, createRoles, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + }; + protectUpdateResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const fields = acm.getResources(); + const updateDeleteRoles = [...new Set([...acm.getRolesPerOperation('update'), ...acm.getRolesPerOperation('delete')])]; + // protect fields to be updated and fields that can't be set to null (partial delete on fields) + const totalRoles = updateDeleteRoles.map(role => { + const allowedFields = fields.filter(resource => acm.isAllowed(role, resource, 'update')); + const nullAllowedFileds = fields.filter(resource => acm.isAllowed(role, resource, 'delete')); + const roleDefinition = this.roleMap.get(role)!; + roleDefinition.allowedFields = allowedFields.length === fields.length ? [] : allowedFields; + roleDefinition.nullAllowedFields = nullAllowedFileds.length === fields.length ? [] : nullAllowedFileds; + return roleDefinition; + }); + const datasource = ctx.api.host.getDataSource(`${def.name.value}Table`) as DataSourceProvider; + const requestExpression = generateAuthRequestExpression(); + const authExpression = generateAuthExpressionForUpdate(this.configuredAuthProviders, totalRoles, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(requestExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`), + datasource, + ); + }; + + protectDeleteResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + // only roles with full delete on every field can delete + const deleteRoles = acm.getRolesPerOperation('delete', true).map(role => this.roleMap.get(role)!); + const datasource = ctx.api.host.getDataSource(`${def.name.value}Table`) as DataSourceProvider; + const requestExpression = generateAuthRequestExpression(); + const authExpression = geneateAuthExpressionForDelete(this.configuredAuthProviders, deleteRoles, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(requestExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`), + datasource, + ); + }; + + protectSubscriptionResolver = ( + ctx: TransformerContextProvider, + typeName: string, + fieldName: string, + subscriptionRoles: Array, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const authExpression = generateAuthExpressionForSubscriptions(this.configuredAuthProviders, subscriptionRoles); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + }; + + /* + Role Helpers + */ + private convertRulesToRoles(acm: AccessControlMatrix, authRules: AuthRule[], field?: string, overideOperations?: ModelOperation[]) { + for (let rule of authRules) { + let operations: ModelOperation[] = overideOperations ? overideOperations : rule.operations || MODEL_OPERATIONS; + if (rule.groups && !rule.groupsField) { + rule.groups.forEach(group => { + const groupClaim = rule.groupClaim || DEFAULT_GROUP_CLAIM; + const roleName = `${rule.provider}:staticGroup:${group}:${groupClaim}`; + if (!(roleName in this.roleMap)) { + this.roleMap.set(roleName, { + provider: rule.provider!, + strategy: rule.allow, + static: true, + claim: groupClaim, + entity: group, + }); + } + acm.setRole({ role: roleName, resource: field, operations }); + }); + } else { + let roleName: string; + let roleDefinition: RoleDefinition; + switch (rule.provider) { + case 'apiKey': + roleName = 'apiKey:public'; + roleDefinition = { provider: rule.provider, strategy: rule.allow, static: true }; + break; + case 'function': + roleName = 'function:custom'; + roleDefinition = { provider: rule.provider, strategy: rule.allow, static: true }; + break; + case 'iam': + roleName = `iam:${rule.allow}`; + roleDefinition = { + provider: rule.provider, + strategy: rule.allow, + static: true, + claim: rule.allow === 'private' ? 'authRole' : 'unauthRole', + }; + break; + case 'oidc': + case 'userPools': + if (rule.allow === 'groups') { + const groupClaim = rule.groupClaim || DEFAULT_GROUP_CLAIM; + const groupsField = rule.groupsField || DEFAULT_GROUPS_FIELD; + roleName = `${rule.provider}:dynamicGroup:${groupsField}:${groupClaim}`; + roleDefinition = { + provider: rule.provider, + strategy: rule.allow, + static: false, + claim: groupClaim, + entity: groupsField, + }; + } else if (rule.allow === 'owner') { + const ownerField = rule.ownerField || DEFAULT_OWNER_FIELD; + const ownerClaim = rule.identityClaim || DEFAULT_IDENTITY_CLAIM; + roleName = `${rule.provider}:owner:${ownerField}:${ownerClaim}`; + roleDefinition = { + provider: rule.provider, + strategy: rule.allow, + static: false, + claim: ownerClaim, + entity: ownerField, + }; + } else if (rule.allow === 'private') { + roleName = `${rule.provider}:${rule.allow}`; + roleDefinition = { + provider: rule.provider, + strategy: rule.allow, + static: true, + }; + } else { + throw new TransformerContractError(`Could not create a role from ${JSON.stringify(rule)}`); + } + break; + default: + throw new TransformerContractError(`Could not create a role from ${JSON.stringify(rule)}`); + } + if (!(roleName in this.roleMap)) { + this.roleMap.set(roleName, roleDefinition); + } + acm.setRole({ role: roleName, resource: field, operations }); + } + } + } + + private doesTypeHaveRulesForOperation(acm: AccessControlMatrix, operation: ModelOperation) { + const rolesHasDefaultProvider = (roles: Array) => { + return roles.some(r => this.roleMap.get(r)!.provider! === this.configuredAuthProviders.default); + }; + const roles = acm.getRolesPerOperation(operation, operation === 'delete'); + return rolesHasDefaultProvider(roles) || (roles.length === 0 && this.shouldAddDefaultServiceDirective()); + } + private getAuthProviders(roles: Array): Array { + const providers: Set = new Set(); + // get the roles created for type + for (let role of roles) { + providers.add(this.roleMap.get(role)!.provider); + } + if (this.configuredAuthProviders.hasAdminUIEnabled) { + providers.add('iam'); + } + return Array.from(providers); + } + + /* + Schema Generation Helpers + */ + private addFieldsToObject(ctx: TransformerTransformSchemaStepContextProvider, modelName: string, ownerFields: Array) { + const modelObject = ctx.output.getObject(modelName)!; + const existingFields = collectFieldNames(modelObject); + const ownerFieldsToAdd = ownerFields.filter(field => !existingFields.includes(field)); + for (let ownerField of ownerFieldsToAdd) { + (modelObject as any).fields.push(makeField(ownerField, [], makeNamedType('String'))); + } + ctx.output.putType(modelObject); + } + private propagateAuthDirectivesToNestedTypes( + ctx: TransformerTransformSchemaStepContextProvider, + def: ObjectTypeDefinitionNode, + providers: Array, + ) { + const nonModelTypePredicate = (fieldType: TypeDefinitionNode): TypeDefinitionNode | undefined => { + if (fieldType) { + if (fieldType.kind !== 'ObjectTypeDefinition') { + return undefined; + } + const typeModel = fieldType.directives!.find(dir => dir.name.value === 'model'); + return typeModel !== undefined ? undefined : fieldType; + } + return fieldType; + }; + const nonModelFieldTypes = def + .fields!.map(f => ctx.output.getType(getBaseType(f.type)) as TypeDefinitionNode) + .filter(nonModelTypePredicate); + for (const nonModelFieldType of nonModelFieldTypes) { + const nonModelName = nonModelFieldType.name.value; + const hasSeenType = this.seenNonModelTypes.has(nonModelFieldType.name.value); + let directives = this.getServiceDirectives(providers, hasSeenType); + if (!hasSeenType) { + this.seenNonModelTypes.set(nonModelName, new Set([...directives.map(dir => dir.name.value)])); + // since we haven't seen this type before we add it to the iam policy resource sets + const hasIAM = directives.some(dir => dir.name.value === 'aws_iam') || this.configuredAuthProviders.default === 'iam'; + if (hasIAM) { + this.unauthPolicyResources.add(`${nonModelFieldType.name.value}/null`); + this.authPolicyResources.add(`${nonModelFieldType.name.value}/null`); + } + } else { + const currentDirectives = this.seenNonModelTypes.get(nonModelName)!; + directives = directives.filter(dir => !currentDirectives.has(dir.name.value)); + this.seenNonModelTypes.set(nonModelName, new Set([...directives.map(dir => dir.name.value), ...currentDirectives])); + } + // we continue to check the nested types if we find that directives list is not empty or if haven't seen the type before + if (directives.length > 0 || !hasSeenType) { + extendTypeWithDirectives(ctx, nonModelFieldType.name.value, directives); + this.propagateAuthDirectivesToNestedTypes(ctx, nonModelFieldType, providers); + } + } + } + + private getServiceDirectives(providers: Readonly>, addDefaultIfNeeded: boolean = true): Array { + if (providers.length === 0) { + return []; + } + const directives: Array = new Array(); + /* + We only add a service directive if it's not the default or + it's the default but there are other rules under different providers. + For fields we don't we don't add the default since it would open up access. + */ + const addDirectiveIfNeeded = (provider: AuthProvider, directiveName: string): void => { + if ( + (this.configuredAuthProviders.default !== provider && providers.some(p => p === provider)) || + (this.configuredAuthProviders.default === provider && providers.some(p => p !== provider && addDefaultIfNeeded === true)) + ) { + directives.push(makeDirective(directiveName, [])); + } + }; + + for (let [authProvider, directiveName] of AUTH_PROVIDER_DIRECTIVE_MAP) { + addDirectiveIfNeeded(authProvider, directiveName); + } + /* + If we have any rules for the default provider AND those with other providers, + we add the default provider directive, regardless of the addDefaultDirective value + + For example if we have this rule and the default is API_KEY + @auth(rules: [{ allow: owner }, { allow: public, operations: [read] }]) + + Then we need to add @aws_api_key on the queries along with @aws_cognito_user_pools, but we + cannot add @aws_api_key to other operations since their is no rule granted access to it + */ + if ( + providers.some(p => p === this.configuredAuthProviders.default) && + providers.some(p => p !== this.configuredAuthProviders.default) && + !directives.some(d => d.name.value === AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default)) + ) { + directives.push(makeDirective(AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default) as string, [])); + } + return directives; + } + /** + * When AdminUI is enabled, all the types and operations get IAM auth. If the default auth mode is + * not IAM all the fields will need to have the default auth mode directive to ensure both IAM and deault + * auth modes are allowed to access + * default auth provider needs to be added if AdminUI is enabled and default auth type is not IAM + * @returns boolean + */ + private shouldAddDefaultServiceDirective(): boolean { + return this.configuredAuthProviders.hasAdminUIEnabled && this.config.authConfig.defaultAuthentication.authenticationType !== 'AWS_IAM'; + } + /* + IAM Helpers + */ + private generateIAMPolicies(ctx: TransformerContextProvider) { + // iam + if (this.generateIAMPolicyforAuthRole) { + // Sanity check to make sure we're not generating invalid policies, where no resources are defined. + if (this.authPolicyResources.size === 0) { + // When AdminUI is enabled, IAM auth is added but it does not need any policies to be generated + if (!this.configuredAuthProviders.hasAdminUIEnabled) { + throw new TransformerContractError('AuthRole policies should be generated, but no resources were added.'); + } + } else { + const authRoleParameter = (ctx.stackManager.getParameter(IAM_AUTH_ROLE_PARAMETER) as cdk.CfnParameter).valueAsString; + const authPolicyDocuments = createPolicyDocumentForManagedPolicy(this.authPolicyResources); + const rootStack = ctx.stackManager.rootStack; + for (let i = 0; i < authPolicyDocuments.length; i++) { + const paddedIndex = `${i + 1}`.padStart(2, '0'); + const resourceName = `${ResourceConstants.RESOURCES.AuthRolePolicy}${paddedIndex}`; + new iam.ManagedPolicy(rootStack, resourceName, { + document: iam.PolicyDocument.fromJson(authPolicyDocuments[i]), + // we need to add the arn path as this is something cdk is looking for when using imported roles in policies + roles: [ + iam.Role.fromRoleArn( + rootStack, + 'auth-role-name', + `arn:aws:iam::${cdk.Stack.of(rootStack).account}:role/${authRoleParameter}`, + ), + ], + }); + } + } + } + if (this.generateIAMPolicyforUnauthRole) { + // Sanity check to make sure we're not generating invalid policies, where no resources are defined. + if (this.unauthPolicyResources.size === 0) { + throw new TransformerContractError('UnauthRole policies should be generated, but no resources were added'); + } + const unauthRoleParameter = (ctx.stackManager.getParameter(IAM_UNAUTH_ROLE_PARAMETER) as cdk.CfnParameter).valueAsString; + const unauthPolicyDocuments = createPolicyDocumentForManagedPolicy(this.unauthPolicyResources); + const rootStack = ctx.stackManager.rootStack; + for (let i = 0; i < unauthPolicyDocuments.length; i++) { + const paddedIndex = `${i + 1}`.padStart(2, '0'); + const resourceName = `${ResourceConstants.RESOURCES.UnauthRolePolicy}${paddedIndex}`; + new iam.ManagedPolicy(ctx.stackManager.rootStack, resourceName, { + document: iam.PolicyDocument.fromJson(unauthPolicyDocuments[i]), + roles: [ + iam.Role.fromRoleArn( + rootStack, + 'unauth-role-name', + `arn:aws:iam::${cdk.Stack.of(rootStack).account}:role/${unauthRoleParameter}`, + ), + ], + }); + } + } + } + private setAuthPolicyFlag(rules: AuthRule[]): void { + if (rules.length === 0 || this.generateIAMPolicyforAuthRole === true) { + return; + } + for (const rule of rules) { + if ((rule.allow === 'private' || rule.allow === 'public') && rule.provider === 'iam') { + this.generateIAMPolicyforAuthRole = true; + return; + } + } + } + + private setUnauthPolicyFlag(rules: AuthRule[]): void { + if (rules.length === 0 || this.generateIAMPolicyforUnauthRole === true) { + return; + } + for (const rule of rules) { + if (rule.allow === 'public' && rule.provider === 'iam') { + this.generateIAMPolicyforUnauthRole = true; + return; + } + } + } + + private addOperationToResourceReferences(operationName: string, fieldName: string, roles: Array) { + const iamPublicRolesExist = roles.some(r => this.roleMap.get(r)!.provider === 'iam' && this.roleMap.get(r)!.strategy === 'public'); + const iamPrivateRolesExist = roles.some(r => this.roleMap.get(r)!.provider === 'iam' && this.roleMap.get(r)!.strategy === 'private'); + + if (iamPublicRolesExist) { + this.unauthPolicyResources.add(`${operationName}/${fieldName}`); + this.authPolicyResources.add(`${operationName}/${fieldName}`); + } + if (iamPrivateRolesExist) { + this.authPolicyResources.add(`${operationName}/${fieldName}`); + } + } + + /** + * TODO: Change Resource Ref Object/Field Functions to work with roles + */ + private addTypeToResourceReferences(typeName: string, rules: AuthRule[]): void { + const iamPublicRulesExist = rules.some(r => r.allow === 'public' && r.provider === 'iam' && r.generateIAMPolicy); + const iamPrivateRulesExist = rules.some(r => r.allow === 'private' && r.provider === 'iam' && r.generateIAMPolicy); + + if (iamPublicRulesExist) { + this.unauthPolicyResources.add(`${typeName}/null`); + this.authPolicyResources.add(`${typeName}/null`); + } + if (iamPrivateRulesExist) { + this.authPolicyResources.add(`${typeName}/null`); + } + } + + private addFieldToResourceReferences(typeName: string, fieldName: string, rules: AuthRule[]): void { + const iamPublicRulesExist = rules.some(r => r.allow === 'public' && r.provider === 'iam' && r.generateIAMPolicy); + const iamPrivateRulesExist = rules.some(r => r.allow === 'private' && r.provider === 'iam' && r.generateIAMPolicy); + + if (iamPublicRulesExist) { + this.unauthPolicyResources.add(`${typeName}/${fieldName}`); + this.authPolicyResources.add(`${typeName}/${fieldName}`); + } + if (iamPrivateRulesExist) { + this.authPolicyResources.add(`${typeName}/${fieldName}`); + } + } +} diff --git a/packages/amplify-graphql-auth-transformer/src/index.ts b/packages/amplify-graphql-auth-transformer/src/index.ts new file mode 100644 index 00000000000..5382eacf29f --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/index.ts @@ -0,0 +1,4 @@ +export * from './graphql-auth-transformer'; +export * from './utils/constants'; +export * from './utils/definitions'; +export { AccessControlMatrix } from './accesscontrol'; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/field.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/field.ts new file mode 100644 index 00000000000..e5ec9ffe460 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/field.ts @@ -0,0 +1,136 @@ +import { OPERATION_KEY } from '@aws-amplify/graphql-model-transformer'; +import { FieldDefinitionNode } from 'graphql'; +import { + Expression, + iff, + not, + ref, + equals, + str, + compoundExpression, + printBlock, + toJson, + set, + methodCall, + nul, + ifElse, + bool, + raw, + forEach, +} from 'graphql-mapping-template'; +import { + RoleDefinition, + splitRoles, + COGNITO_AUTH_TYPE, + OIDC_AUTH_TYPE, + ConfiguredAuthProviders, + fieldIsList, + IS_AUTHORIZED_FLAG, +} from '../utils'; +import { getOwnerClaim, generateStaticRoleExpression, apiKeyExpression, iamExpression, emptyPayload } from './helpers'; + +// Field Read VTL Functions +const generateDynamicAuthReadExpression = (roles: Array, fields: ReadonlyArray) => { + const ownerExpressions = new Array(); + const dynamicGroupExpressions = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpressions.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`ownerEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.source.${role.entity!}`), nul())), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff( + equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ] + : [iff(equals(ref(`ownerEntity${idx}`), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true)))]), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpressions.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`groupEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.source.${role.entity!}`), nul())), + set(ref(`groupClaim${idx}`), getOwnerClaim(role.claim!)), + forEach(ref('userGroup'), ref('dynamicGroupClaim'), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ]), + ), + ); + } + }); + return [...(ownerExpressions.length > 0 || dynamicGroupExpressions.length > 0 ? [...ownerExpressions, ...dynamicGroupExpressions] : [])]; +}; + +export const generateAuthExpressionForField = ( + provider: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +): string => { + const { cogntoStaticRoles, cognitoDynamicRoles, oidcStaticRoles, oidcDynamicRoles, iamRoles, apiKeyRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [set(ref(IS_AUTHORIZED_FLAG), bool(false))]; + if (provider.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (provider.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, provider.hasAdminUIEnabled, provider.adminUserPoolID)); + } + if (provider.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(cogntoStaticRoles), + ...generateDynamicAuthReadExpression(cognitoDynamicRoles, fields), + ]), + ), + ); + } + if (provider.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(oidcStaticRoles), + ...generateDynamicAuthReadExpression(oidcDynamicRoles, fields), + ]), + ), + ); + } + totalAuthExpressions.push(iff(not(ref(IS_AUTHORIZED_FLAG)), ref('util.unauthorized()'))); + return printBlock('Field Authorization Steps')(compoundExpression([...totalAuthExpressions, emptyPayload])); +}; + +/** + * This is the response resolver for fields to protect subscriptions + * @param subscriptionsEnabled + * @returns + */ +export const generateFieldAuthResponse = (operation: string, fieldName: string, subscriptionsEnabled: boolean): string => { + if (subscriptionsEnabled) { + return printBlock('Checking for allowed operations which can return this field')( + compoundExpression([ + set(ref('operation'), methodCall(ref('util.defaultIfNull'), methodCall(ref('ctx.source.get'), str(OPERATION_KEY)), nul())), + ifElse(equals(ref('operation'), str(operation)), toJson(nul()), toJson(ref(`context.source.${fieldName}`))), + ]), + ); + } + return printBlock('Return Source Field')(toJson(ref(`context.source.${fieldName}`))); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts new file mode 100644 index 00000000000..c121997e460 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts @@ -0,0 +1,159 @@ +import { + qref, + Expression, + ifElse, + iff, + methodCall, + not, + ref, + set, + str, + raw, + obj, + bool, + compoundExpression, + printBlock, + toJson, + forEach, + list, + equals, + or, +} from 'graphql-mapping-template'; +import { NONE_VALUE } from 'graphql-transformer-common'; +import { + DEFAULT_COGNITO_IDENTITY_CLAIM, + RoleDefinition, + IS_AUTHORIZED_FLAG, + ALLOWED_FIELDS, + API_KEY_AUTH_TYPE, + LAMBDA_AUTH_TYPE, + ADMIN_ROLE, + IAM_AUTH_TYPE, + MANAGE_ROLE, +} from '../utils'; + +// note in the resolver that operation is protected by auth +export const setHasAuthExpression: Expression = qref(methodCall(ref('ctx.stash.put'), str('hasAuth'), bool(true))); + +// since the keySet returns a set we can convert it to a list by converting to json and parsing back as a list +export const getInputFields = (): Expression => { + return set(ref('inputFields'), methodCall(ref('util.parseJson'), methodCall(ref('util.toJson'), ref('ctx.args.input.keySet()')))); +}; + +export const getIdentityClaimExp = (value: Expression, defaultValueExp: Expression): Expression => { + return methodCall(ref('util.defaultIfNull'), methodCall(ref('ctx.identity.claims.get'), value), defaultValueExp); +}; + +// for create mutations and subscriptions +export const addAllowedFieldsIfElse = (fieldKey: string, breakLoop: boolean = false): Expression => { + return ifElse( + not(ref(`${fieldKey}.isEmpty()`)), + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref(fieldKey))), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), ...(breakLoop ? [raw('#break')] : [])]), + ); +}; + +// iam check +export const iamCheck = (claim: string, exp: Expression) => iff(equals(ref('ctx.identity.userArn'), ref(`ctx.stash.${claim}`)), exp); + +/** + * Behavior of auth v1 + * Order of how the owner value is retrieved from the jwt + * if claim is username + * 1. username + * 2. cognito:username + * 3. none value + * + * if claim is custom + * 1. custom + * 2. none value + */ +export const getOwnerClaim = (ownerClaim: string): Expression => { + if (ownerClaim === 'username') { + return getIdentityClaimExp(str(ownerClaim), getIdentityClaimExp(str(DEFAULT_COGNITO_IDENTITY_CLAIM), str(NONE_VALUE))); + } + return getIdentityClaimExp(str(ownerClaim), str(NONE_VALUE)); +}; + +export const responseCheckForErrors = () => + iff(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type'))); + +// Common Expressions + +export const generateStaticRoleExpression = (roles: Array): Array => { + const staticRoleExpression: Array = new Array(); + const privateRoleIdx = roles.findIndex(r => r.strategy === 'private'); + if (privateRoleIdx > -1) { + staticRoleExpression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + roles.splice(privateRoleIdx, 1); + } + if (roles.length > 0) { + staticRoleExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), + forEach(ref('groupRole'), ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw(`#break`)]), + ), + ]), + ]), + ), + ); + } + return staticRoleExpression; +}; + +export const apiKeyExpression = (roles: Array) => + iff( + equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), + compoundExpression([...(roles.length > 0 ? [set(ref(IS_AUTHORIZED_FLAG), bool(true))] : [])]), + ); + +export const lambdaExpression = (roles: Array) => + iff( + equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), + compoundExpression([...(roles.length > 0 ? [set(ref(IS_AUTHORIZED_FLAG), bool(true))] : [])]), + ); + +export const iamExpression = (roles: Array, adminuiEnabled: boolean = false, adminUserPoolID?: string) => { + const expression = new Array(); + // allow if using admin ui + if (adminuiEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID}${ADMIN_ROLE}`)), + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID}${MANAGE_ROLE}`)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + expression.push(iff(not(ref(IS_AUTHORIZED_FLAG)), iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))))); + } + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +// Get Request for Update and Delete +export const generateAuthRequestExpression = () => { + const statements = [ + set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('GetItem') })), + ifElse( + ref('ctx.stash.metadata.modelObjectKey'), + set(ref('key'), ref('ctx.stash.metadata.modelObjectKey')), + compoundExpression([set(ref('key'), obj({ id: methodCall(ref('util.dynamodb.toDynamoDB'), ref('ctx.args.input.id')) }))]), + ), + qref(methodCall(ref('GetRequest.put'), str('key'), ref('key'))), + toJson(ref('GetRequest')), + ]; + return printBlock('Get Request template')(compoundExpression(statements)); +}; + +export const emptyPayload = toJson(raw(JSON.stringify({ version: '2018-05-29', payload: {} }))); diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts new file mode 100644 index 00000000000..5f9e8476d39 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts @@ -0,0 +1,7 @@ +export { generateAuthExpressionForQueries } from './query'; +export { generateAuthExpressionForCreate } from './mutation.create'; +export { generateAuthExpressionForUpdate } from './mutation.update'; +export { geneateAuthExpressionForDelete } from './mutation.delete'; +export { generateAuthExpressionForField, generateFieldAuthResponse } from './field'; +export { generateAuthExpressionForSubscriptions } from './subscriptions'; +export { generateAuthRequestExpression } from './helpers'; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts new file mode 100644 index 00000000000..9b65f6f8f68 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts @@ -0,0 +1,297 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + Expression, + compoundExpression, + set, + ref, + bool, + raw, + iff, + and, + isNullOrEmpty, + not, + methodCall, + qref, + list, + nul, + forEach, + equals, + str, + or, + printBlock, +} from 'graphql-mapping-template'; +import { + getOwnerClaim, + getIdentityClaimExp, + getInputFields, + addAllowedFieldsIfElse, + emptyPayload, + setHasAuthExpression, + iamCheck, +} from './helpers'; +import { + ADMIN_ROLE, + API_KEY_AUTH_TYPE, + LAMBDA_AUTH_TYPE, + COGNITO_AUTH_TYPE, + ConfiguredAuthProviders, + IAM_AUTH_TYPE, + MANAGE_ROLE, + OIDC_AUTH_TYPE, + RoleDefinition, + splitRoles, + fieldIsList, + IS_AUTHORIZED_FLAG, + ALLOWED_FIELDS, + DENIED_FIELDS, +} from '../utils'; + +/** + * There is only one role for ApiKey we can use the first index + * @param roles + * @returns Expression | null + */ +const apiKeyExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles[0].allowedFields!.length > 0) { + expression.push(set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].allowedFields)))); + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), compoundExpression(expression)); +}; + +/** + * No need to combine allowed fields as the request can only be signed by one iam role + * @param roles + * @returns + */ +const iamExpression = (roles: Array, hasAdminUIEnabled: boolean = false, adminUserPoolID?: string) => { + const expression = new Array(); + // allow if using admin ui + if (hasAdminUIEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID!}${ADMIN_ROLE}`)), + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID!}${MANAGE_ROLE}`)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + if (role.allowedFields!.length > 0) { + expression.push( + iamCheck(role.claim!, compoundExpression([set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(role.allowedFields)))])), + ); + } else { + expression.push(iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true)))); + } + } + } else { + expression.push(ref('util.unauthorized()')); + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +/** + * There is only one role for Lambda we can use the first index + * @param roles + * @returns Expression | null + */ + const lambdaExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles[0].allowedFields!.length > 0) { + expression.push(set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].allowedFields)))); + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), compoundExpression(expression)); +}; + +const generateStaticRoleExpression = (roles: Array): Array => { + const staticRoleExpression: Array = new Array(); + const privateRoleIdx = roles.findIndex(r => r.strategy === 'private'); + if (privateRoleIdx > -1) { + const privateRole = roles[privateRoleIdx]; + if (privateRole.allowedFields!.length > 0) { + staticRoleExpression.push(qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), raw(JSON.stringify(privateRole.allowedFields))))); + } else { + staticRoleExpression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + roles.splice(privateRoleIdx, 1); + } + if (roles.length > 0) { + staticRoleExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref('staticGroupRoles'), + raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity, allowedFields: r.allowedFields ?? [] })))), + ), + forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + addAllowedFieldsIfElse('groupRole.allowedFields', true), + ), + ]), + ]), + ), + ); + } + return staticRoleExpression; +}; + +const dynamicGroupRoleExpression = (roles: Array, fields: ReadonlyArray): Array => { + const ownerExpression = new Array(); + const dynamicGroupExpression = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`ownerEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.args.input.${role.entity!}`), entityIsList ? list([]) : nul()), + ), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + set(ref(`ownerAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff(equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), addAllowedFieldsIfElse(`ownerAllowedFields${idx}`, true)), + ]), + ] + : [iff(equals(ref(`ownerClaim${idx}`), ref(`ownerEntity${idx}`)), addAllowedFieldsIfElse(`ownerAllowedFields${idx}`))]), + iff( + and([isNullOrEmpty(ref(`ownerEntity${idx}`)), not(methodCall(ref('ctx.args.input.containsKey'), str(role.entity!)))]), + compoundExpression([ + qref( + methodCall( + ref('ctx.args.input.put'), + str(role.entity!), + entityIsList ? list([ref(`ownerClaim${idx}`)]) : ref(`ownerClaim${idx}`), + ), + ), + addAllowedFieldsIfElse(`ownerAllowedFields${idx}`), + ]), + ), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`groupEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.args.input.${role.entity!}`), entityIsList ? list([]) : nul()), + ), + set(ref(`groupClaim${idx}`), getIdentityClaimExp(str(role.claim!), list([]))), + set(ref(`groupAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + forEach(ref('userGroup'), ref(`groupClaim${idx}`), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + addAllowedFieldsIfElse(`groupAllowedFields${idx}`, true), + ), + ]), + ]), + ), + ); + } + }); + + return [...(ownerExpression.length > 0 ? ownerExpression : []), ...(dynamicGroupExpression.length > 0 ? dynamicGroupExpression : [])]; +}; + +/** + * Unauthorized if + * - auth conditions could not be met + * - there are fields conditions that could not be met + * @param providers + * @param roles + * @param fields + * @returns + */ +export const generateAuthExpressionForCreate = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +): string => { + const { + cogntoStaticRoles: cognitoStaticGroupRoles, + cognitoDynamicRoles, + oidcStaticRoles: oidcStaticGroupRoles, + oidcDynamicRoles, + apiKeyRoles, + iamRoles, + lambdaRoles, + } = splitRoles(roles); + const totalAuthExpressions: Array = [ + setHasAuthExpression, + getInputFields(), + set(ref(IS_AUTHORIZED_FLAG), bool(false)), + set(ref(ALLOWED_FIELDS), list([])), + ]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled, providers.adminUserPoolID)); + } + if (providers.hasLambda) { + totalAuthExpressions.push(lambdaExpression(lambdaRoles)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(cognitoStaticGroupRoles), + ...dynamicGroupRoleExpression(cognitoDynamicRoles, fields), + ]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(oidcStaticGroupRoles), + ...dynamicGroupRoleExpression(oidcDynamicRoles, fields), + ]), + ), + ); + } + totalAuthExpressions.push( + iff(and([not(ref(IS_AUTHORIZED_FLAG)), ref(`${ALLOWED_FIELDS}.isEmpty()`)]), ref('util.unauthorized()')), + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(DENIED_FIELDS), methodCall(ref('util.list.copyAndRemoveAll'), ref('inputFields'), ref(ALLOWED_FIELDS))), + iff( + ref(`${DENIED_FIELDS}.size() > 0`), + methodCall(ref('util.error'), str(`Unauthorized on \${${DENIED_FIELDS}}`), str('Unauthorized')), + ), + ]), + ), + ); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, emptyPayload])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts new file mode 100644 index 00000000000..573afd7d8d7 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts @@ -0,0 +1,209 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + Expression, + printBlock, + compoundExpression, + bool, + equals, + iff, + raw, + ref, + set, + str, + methodCall, + or, + forEach, + list, + not, + nul, +} from 'graphql-mapping-template'; +import { emptyPayload, getIdentityClaimExp, getOwnerClaim, iamCheck, setHasAuthExpression } from './helpers'; +import { + ADMIN_ROLE, + API_KEY_AUTH_TYPE, + COGNITO_AUTH_TYPE, + LAMBDA_AUTH_TYPE, + ConfiguredAuthProviders, + fieldIsList, + IAM_AUTH_TYPE, + IS_AUTHORIZED_FLAG, + MANAGE_ROLE, + OIDC_AUTH_TYPE, + RoleDefinition, + splitRoles, +} from '../utils'; + +/** + * There is only one role for ApiKey we can use the first index + * @param roles + * @returns Expression | null + */ +const apiKeyExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles.length > 0) { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), compoundExpression(expression)); +}; +/** + * No need to combine allowed fields as the request can only be signed by one iam role + * @param roles + * @returns + */ +const iamExpression = (roles: Array, hasAdminUIEnabled: boolean = false, adminUserPoolID?: string) => { + const expression = new Array(); + // allow if using admin ui + if (hasAdminUIEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID}${ADMIN_ROLE}`)), + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID}${MANAGE_ROLE}`)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + } else { + expression.push(ref('util.unauthorized()')); + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +/** + * There is only one role for Lambda we can use the first index + * @param roles + * @returns Expression | null + */ + const lambdaExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles.length > 0) { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), compoundExpression(expression)); +}; + +const generateStaticRoleExpression = (roles: Array): Array => { + const staticRoleExpression: Array = new Array(); + const privateRoleIdx = roles.findIndex(r => r.strategy === 'private'); + if (privateRoleIdx > -1) { + staticRoleExpression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + roles.splice(privateRoleIdx, -1); + } + if (roles.length > 0) { + staticRoleExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), + forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ]), + ), + ); + } + return staticRoleExpression; +}; + +const dynamicGroupRoleExpression = (roles: Array, fields: ReadonlyArray) => { + const ownerExpression = new Array(); + const dynamicGroupExpression = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`ownerEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity!}`), nul())), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff(equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true))), + ]), + ] + : [iff(equals(ref(`ownerEntity${idx}`), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true)))]), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`groupEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity}`), entityIsList ? list([]) : nul()), + ), + set(ref(`groupClaim${idx}`), getIdentityClaimExp(str(role.claim!), list([]))), + forEach(ref('userGroup'), ref(`groupClaim${idx}`), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + set(ref(IS_AUTHORIZED_FLAG), bool(true)), + ), + ]), + ]), + ), + ); + } + }); + return [...(ownerExpression.length > 0 ? ownerExpression : []), ...(dynamicGroupExpression.length > 0 ? dynamicGroupExpression : [])]; +}; + +export const geneateAuthExpressionForDelete = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +) => { + const { cogntoStaticRoles, cognitoDynamicRoles, oidcStaticRoles, oidcDynamicRoles, apiKeyRoles, iamRoles, lambdaRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [setHasAuthExpression, set(ref(IS_AUTHORIZED_FLAG), bool(false))]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled, providers.adminUserPoolID)); + } + if (providers.hasLambda) { + totalAuthExpressions.push(lambdaExpression(lambdaRoles)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(cogntoStaticRoles), + ...dynamicGroupRoleExpression(cognitoDynamicRoles, fields), + ]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...generateStaticRoleExpression(oidcStaticRoles), ...dynamicGroupRoleExpression(oidcDynamicRoles, fields)]), + ), + ); + } + totalAuthExpressions.push(iff(not(ref(IS_AUTHORIZED_FLAG)), ref('util.unauthorized()'))); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, emptyPayload])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts new file mode 100644 index 00000000000..d0c2cbed6ed --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts @@ -0,0 +1,343 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + compoundExpression, + iff, + raw, + set, + ref, + forEach, + bool, + Expression, + not, + obj, + list, + qref, + equals, + str, + and, + methodCall, + toJson, + printBlock, + ifElse, + nul, + or, +} from 'graphql-mapping-template'; +import { + ADMIN_ROLE, + API_KEY_AUTH_TYPE, + COGNITO_AUTH_TYPE, + LAMBDA_AUTH_TYPE, + ConfiguredAuthProviders, + IAM_AUTH_TYPE, + MANAGE_ROLE, + OIDC_AUTH_TYPE, + RoleDefinition, + splitRoles, + fieldIsList, + IS_AUTHORIZED_FLAG, + ALLOWED_FIELDS, + NULL_ALLOWED_FIELDS, + DENIED_FIELDS, +} from '../utils'; +import { getIdentityClaimExp, responseCheckForErrors, getOwnerClaim, getInputFields, setHasAuthExpression, iamCheck } from './helpers'; + +/** + * There is only one role for ApiKey we can use the first index + * @param roles + * @returns Expression | null + */ +const apiKeyExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles[0].allowedFields!.length > 0 || roles[0].nullAllowedFields!.length > 0) { + expression.push( + set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].allowedFields))), + set(ref(`${NULL_ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].nullAllowedFields))), + ); + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), compoundExpression(expression)); +}; + +/** + * There is only one role for Lambda we can use the first index + * @param roles + * @returns Expression | null + */ + const lambdaExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles[0].allowedFields!.length > 0 || roles[0].nullAllowedFields!.length > 0) { + expression.push( + set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].allowedFields))), + set(ref(`${NULL_ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].nullAllowedFields))), + ); + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(LAMBDA_AUTH_TYPE)), compoundExpression(expression)); +}; + +const iamExpression = (roles: Array, hasAdminUIEnabled: boolean = false, adminUserPoolID?: string) => { + const expression = new Array(); + // allow if using admin ui + if (hasAdminUIEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID}${ADMIN_ROLE}`)), + methodCall(ref('ctx.identity.userArn.contains'), str(`${adminUserPoolID}${MANAGE_ROLE}`)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + if (role.allowedFields!.length > 0 || role.nullAllowedFields!.length > 0) { + expression.push( + iamCheck( + role.claim!, + compoundExpression([ + set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(role.allowedFields))), + set(ref(`${NULL_ALLOWED_FIELDS}`), raw(JSON.stringify(role.nullAllowedFields))), + ]), + ), + ); + } else { + iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + } + } else { + expression.push(ref('util.unauthorized()')); + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +const generateStaticRoleExpression = (roles: Array) => { + const staticRoleExpression: Array = new Array(); + const privateRoleIdx = roles.findIndex(r => r.strategy === 'private'); + if (privateRoleIdx > -1) { + const privateRole = roles[privateRoleIdx]; + if (privateRole.allowedFields!.length > 0 || privateRole.nullAllowedFields!.length > 0) { + staticRoleExpression.push( + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), raw(JSON.stringify(privateRole.allowedFields)))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), raw(JSON.stringify(privateRole.nullAllowedFields)))), + ); + } else { + staticRoleExpression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + roles.splice(privateRoleIdx, 1); + } + if (roles.length > 0) { + staticRoleExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref('staticGroupRoles'), + raw( + JSON.stringify( + roles.map(r => ({ + claim: r.claim, + entity: r.entity, + allowedFields: r.allowedFields, + nullAllowedFields: r.nullAllowedFields, + })), + ), + ), + ), + forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([ + // if we find that it's not fully allowed on update (update/delete) we add the field conditions + // otherwise we set to true and break + ifElse( + or([not(ref(`groupRole.allowedFields.isEmpty()`)), not(ref('groupRole.nullAllowedFields.isEmpty()'))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref('groupRole.allowedFields'))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref('groupRole.nullAllowedFields'))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ), + ]), + ]), + ), + ); + } + return staticRoleExpression; +}; +const dynamicGroupRoleExpression = (roles: Array, fields: ReadonlyArray): Array => { + const ownerExpression = new Array(); + const dynamicGroupExpression = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`ownerEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity!}`), entityIsList ? list([]) : nul()), + ), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + set(ref(`ownerAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + set(ref(`ownerNullAllowedFields${idx}`), raw(JSON.stringify(role.nullAllowedFields))), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff( + equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), + ifElse( + or([not(ref(`ownerAllowedFields${idx}.isEmpty()`)), not(ref(`ownerNullAllowedFields${idx}.isEmpty()`))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref(`ownerAllowedFields${idx}`))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref(`ownerNullAllowedFields${idx}`))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ), + ]), + ] + : [ + iff( + equals(ref(`ownerEntity${idx}`), ref(`ownerClaim${idx}`)), + ifElse( + or([not(ref(`ownerAllowedFields${idx}.isEmpty()`)), not(ref(`ownerNullAllowedFields${idx}.isEmpty()`))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref(`ownerAllowedFields${idx}`))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref(`ownerNullAllowedFields${idx}`))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true))]), + ), + ), + ]), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`groupEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity}`), entityIsList ? list([]) : nul()), + ), + set(ref(`groupClaim${idx}`), getIdentityClaimExp(str(role.claim!), list([]))), + set(ref(`groupAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + set(ref(`groupNullAllowedFields${idx}`), raw(JSON.stringify(role.nullAllowedFields))), + forEach(ref('userGroup'), ref(`groupClaim${idx}`), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + ifElse( + or([not(ref(`groupAllowedFields${idx}.isEmpty()`)), not(ref(`groupNullAllowedFields${idx}.isEmpty()`))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref('groupRole.allowedFields'))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref('groupRole.nullAllowedFields'))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ), + ]), + ]), + ), + ); + } + }); + return [...(ownerExpression.length > 0 ? ownerExpression : []), ...(dynamicGroupExpression.length > 0 ? dynamicGroupExpression : [])]; +}; + +/** + * For update we need to check for allowed fields and null allowed fields + * unauthorized if + * - none of the roles have been met and there are no field conditions + * - role is partially allowed but the field conditions have not been met + * @param providers + * @param roles + * @param fields + * @returns + */ +export const generateAuthExpressionForUpdate = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +) => { + const { cogntoStaticRoles, cognitoDynamicRoles, oidcStaticRoles, oidcDynamicRoles, apiKeyRoles, iamRoles, lambdaRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [ + setHasAuthExpression, + responseCheckForErrors(), + getInputFields(), + set(ref(IS_AUTHORIZED_FLAG), bool(false)), + set(ref(`${ALLOWED_FIELDS}`), list([])), + set(ref(`${NULL_ALLOWED_FIELDS}`), list([])), + set(ref(`${DENIED_FIELDS}`), obj({})), + ]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasLambda) { + totalAuthExpressions.push(lambdaExpression(lambdaRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled, providers.adminUserPoolID)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(cogntoStaticRoles), + ...dynamicGroupRoleExpression(cognitoDynamicRoles, fields), + ]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...generateStaticRoleExpression(oidcStaticRoles), ...dynamicGroupRoleExpression(oidcDynamicRoles, fields)]), + ), + ); + } + totalAuthExpressions.push( + iff( + and([not(ref(IS_AUTHORIZED_FLAG)), ref(`${ALLOWED_FIELDS}.isEmpty()`), ref(`${NULL_ALLOWED_FIELDS}.isEmpty()`)]), + ref('util.unauthorized()'), + ), + // if not authorized we check the field conditions + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + forEach(ref('entry'), ref('util.map.copyAndRetainAllKeys($ctx.args.input, $inputFields).entrySet()'), [ + iff( + and([methodCall(ref('util.isNull'), ref('entry.value')), not(ref(`${NULL_ALLOWED_FIELDS}.contains($entry.value)`))]), + qref(methodCall(ref(`${DENIED_FIELDS}.put`), ref('entry.key'), str(''))), + ), + ]), + forEach(ref('deniedField'), ref(`util.list.copyAndRemoveAll($inputFields, \$${ALLOWED_FIELDS})`), [ + qref(methodCall(ref(`${DENIED_FIELDS}.put`), ref('deniedField'), str(''))), + ]), + ]), + ), + iff( + ref(`${DENIED_FIELDS}.keySet().size() > 0`), + methodCall(ref('util.error'), str(`Unauthorized on \${${DENIED_FIELDS}.keySet()}`), str('Unauthorized')), + ), + ); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts new file mode 100644 index 00000000000..927f504bbdd --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts @@ -0,0 +1,208 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + compoundExpression, + Expression, + obj, + printBlock, + and, + equals, + iff, + methodCall, + not, + ref, + str, + bool, + forEach, + list, + qref, + raw, + set, +} from 'graphql-mapping-template'; +import { getIdentityClaimExp, getOwnerClaim, apiKeyExpression, iamExpression, lambdaExpression, emptyPayload, setHasAuthExpression } from './helpers'; +import { + COGNITO_AUTH_TYPE, + OIDC_AUTH_TYPE, + LAMBDA_AUTH_TYPE, + RoleDefinition, + splitRoles, + ConfiguredAuthProviders, + IS_AUTHORIZED_FLAG, + fieldIsList, + QuerySource, +} from '../utils'; +import { InvalidDirectiveError } from '@aws-amplify/graphql-transformer-core'; +import { NONE_VALUE } from 'graphql-transformer-common'; + +const generateStaticRoleExpression = (roles: Array): Array => { + const staticRoleExpression: Array = []; + let privateRoleIdx = roles.findIndex(r => r.strategy === 'private'); + if (privateRoleIdx > -1) { + staticRoleExpression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + roles.splice(privateRoleIdx, 1); + } + if (roles.length > 0) { + staticRoleExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), + forEach(ref('groupRole'), ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw(`#break`)]), + ), + ]), + ]), + ), + ); + } + return staticRoleExpression; +}; + +const generateAuthFilter = ( + roles: Array, + fields: ReadonlyArray, + querySource: QuerySource, +): Array => { + const authFilter = new Array(); + const groupMap = new Map>(); + const groupContainsExpression = new Array(); + if (!(roles.length > 0)) return []; + /** + * if ownerField is string + * ownerField: { eq: "cognito:owner" } + * if ownerField is a List + * ownerField: { contains: "cognito:owner"} + * + * if groupsField is a string + * groupsField: { in: "cognito:groups" } + * if groupsField is a list + * groupsField: { contains: "cognito:groups" } + * */ + if (querySource === 'dynamodb') { + for (let role of roles) { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + const ownerCondition = entityIsList ? 'contains' : 'eq'; + authFilter.push(obj({ [role.entity!]: obj({ [ownerCondition]: getOwnerClaim(role.claim!) }) })); + } + if (role.strategy === 'groups') { + // for fields where the group is a list and the token is a list we must add every group in the claim + if (entityIsList) { + if (groupMap.has(role.claim!)) { + groupMap.get(role.claim).push(role.entity); + } else { + groupMap.set(role.claim!, [role.entity]); + } + } else { + authFilter.push(obj({ [role.entity!]: obj({ in: getIdentityClaimExp(str(role.claim!), list([str(NONE_VALUE)])) }) })); + } + } + } + for (let [groupClaim, fieldList] of groupMap) { + groupContainsExpression.push( + forEach( + ref('group'), + ref(`util.defaultIfNull($ctx.identity.claims.get("${groupClaim}"), ["${NONE_VALUE}"])`), + fieldList.map(field => qref(methodCall(ref('authFilter.add'), raw(`{"${field}": { "contains": $group }}`)))), + ), + ); + } + return [ + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref('authFilter'), list(authFilter)), + ...(groupContainsExpression.length > 0 ? groupContainsExpression : []), + qref(methodCall(ref('ctx.stash.put'), str('authFilter'), raw('{ "or": $authFilter }'))), + ]), + ), + ]; + } + if (querySource === 'opensearch') { + for (let role of roles) { + let claimValue: Expression; + if (role.strategy === 'owner') { + claimValue = getOwnerClaim(role.claim!); + authFilter.push( + obj({ + terms_set: obj({ + [role.entity!]: obj({ + terms: list([claimValue]), + minimum_should_match_script: obj({ source: str('1') }), + }), + }), + }), + ); + } else if (role.strategy === 'groups') { + claimValue = getIdentityClaimExp(str(role.claim!), list([str(NONE_VALUE)])); + authFilter.push( + obj({ + terms_set: obj({ + [role.entity!]: obj({ + terms: claimValue, + minimum_should_match_script: obj({ source: str('1') }), + }), + }), + }), + ); + } + } + return [ + iff( + not(ref(IS_AUTHORIZED_FLAG)), + qref(methodCall(ref('ctx.stash.put'), str('authFilter'), obj({ bool: obj({ should: list(authFilter) }) }))), + ), + ]; + } + throw new InvalidDirectiveError(`Could not generate an auth filter for a ${querySource} datasource.`); +}; + +export const generateAuthExpressionForQueries = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, + querySource: QuerySource = 'dynamodb', +): string => { + const { cogntoStaticRoles, cognitoDynamicRoles, oidcStaticRoles, oidcDynamicRoles, apiKeyRoles, iamRoles, lambdaRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [setHasAuthExpression, set(ref(IS_AUTHORIZED_FLAG), bool(false))]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasLambda) { + totalAuthExpressions.push(lambdaExpression(lambdaRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled, providers.adminUserPoolID)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([ + ...generateStaticRoleExpression(cogntoStaticRoles), + ...generateAuthFilter(cognitoDynamicRoles, fields, querySource), + ]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([ + ...generateAuthFilter(oidcDynamicRoles, fields, querySource), + ...generateStaticRoleExpression(oidcStaticRoles), + ]), + ), + ); + } + totalAuthExpressions.push( + iff( + and([not(ref(IS_AUTHORIZED_FLAG)), methodCall(ref('util.isNullOrEmpty'), methodCall(ref('ctx.stash.get'), str('authFilter')))]), + ref('util.unauthorized()'), + ), + ); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, emptyPayload])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts new file mode 100644 index 00000000000..845b48784bd --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts @@ -0,0 +1,68 @@ +import { + bool, + compoundExpression, + equals, + Expression, + iff, + methodCall, + not, + ref, + set, + str, + list, + nul, + printBlock, +} from 'graphql-mapping-template'; +import { COGNITO_AUTH_TYPE, ConfiguredAuthProviders, IS_AUTHORIZED_FLAG, OIDC_AUTH_TYPE, LAMBDA_AUTH_TYPE, RoleDefinition, splitRoles } from '../utils'; +import { generateStaticRoleExpression, getOwnerClaim, apiKeyExpression, iamExpression, lambdaExpression, emptyPayload } from './helpers'; + +const dynamicRoleExpression = (roles: Array): Array => { + const ownerExpression = new Array(); + // we only check against owner rules which are not list fields + roles.forEach((role, idx) => { + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`ownerEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.args.${role.entity!}`), nul())), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + iff(equals(ref(`ownerEntity${idx}`), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true))), + ]), + ), + ); + } + }); + + return [...(ownerExpression.length > 0 ? ownerExpression : [])]; +}; + +export const generateAuthExpressionForSubscriptions = (providers: ConfiguredAuthProviders, roles: Array): string => { + const { cogntoStaticRoles, cognitoDynamicRoles, oidcStaticRoles, oidcDynamicRoles, iamRoles, apiKeyRoles, lambdaRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [set(ref(IS_AUTHORIZED_FLAG), bool(false)), set(ref('allowedFields'), list([]))]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasLambda) { + totalAuthExpressions.push(lambdaExpression(lambdaRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled, providers.adminUserPoolID)); + } + if (providers.hasUserPools) + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([...generateStaticRoleExpression(cogntoStaticRoles), ...dynamicRoleExpression(cognitoDynamicRoles)]), + ), + ); + if (providers.hasOIDC) + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...generateStaticRoleExpression(oidcStaticRoles), ...dynamicRoleExpression(oidcDynamicRoles)]), + ), + ); + totalAuthExpressions.push(iff(not(ref(IS_AUTHORIZED_FLAG)), ref('util.unauthorized()'))); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, emptyPayload])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/constants.ts b/packages/amplify-graphql-auth-transformer/src/utils/constants.ts new file mode 100644 index 00000000000..0556982e51d --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/utils/constants.ts @@ -0,0 +1,34 @@ +import { AuthProvider, ModelOperation } from './definitions'; +export const DEFAULT_OWNER_FIELD = 'owner'; +export const DEFAULT_GROUPS_FIELD = 'groups'; +export const DEFAULT_IDENTITY_CLAIM = 'username'; +export const DEFAULT_COGNITO_IDENTITY_CLAIM = 'cognito:username'; +export const DEFAULT_GROUP_CLAIM = 'cognito:groups'; +export const ON_CREATE_FIELD = 'onCreate'; +export const ON_UPDATE_FIELD = 'onUpdate'; +export const ON_DELETE_FIELD = 'onDelete'; +export const AUTH_NON_MODEL_TYPES = 'authNonModelTypes'; +export const MODEL_OPERATIONS: ModelOperation[] = ['create', 'read', 'update', 'delete']; +export const AUTH_PROVIDER_DIRECTIVE_MAP = new Map([ + ['apiKey', 'aws_api_key'], + ['iam', 'aws_iam'], + ['oidc', 'aws_oidc'], + ['userPools', 'aws_cognito_user_pools'], + ['function', 'aws_lambda'], +]); +// values for $util.authType() https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html +export const COGNITO_AUTH_TYPE = 'User Pool Authorization'; +export const OIDC_AUTH_TYPE = 'Open ID Connect Authorization'; +export const IAM_AUTH_TYPE = 'IAM Authorization'; +export const LAMBDA_AUTH_TYPE = 'Lambda Authorization'; +export const API_KEY_AUTH_TYPE = 'API Key Authorization'; +// resolver refs +export const IS_AUTHORIZED_FLAG = 'isAuthorized'; +export const ALLOWED_FIELDS = 'allowedFields'; +export const NULL_ALLOWED_FIELDS = 'nullAllowedFields'; +export const DENIED_FIELDS = 'deniedFields'; +// Admin Roles +export const ADMIN_ROLE = '_Full-access/CognitoIdentityCredentials'; +export const MANAGE_ROLE = '_Manage-only/CognitoIdentityCredentials'; +// resolver +export const NONE_DS = 'NONE_DS'; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts b/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts new file mode 100644 index 00000000000..a35c37ee1c3 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts @@ -0,0 +1,103 @@ +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; +export type AuthStrategy = 'owner' | 'groups' | 'public' | 'private' | 'custom'; +export type AuthProvider = 'apiKey' | 'iam' | 'oidc' | 'userPools' | 'function'; +export type ModelQuery = 'get' | 'list'; +export type ModelMutation = 'create' | 'update' | 'delete'; +export type ModelOperation = 'create' | 'update' | 'delete' | 'read'; + +export type QuerySource = 'dynamodb' | 'opensearch'; +export interface SearchableConfig { + queries: { + search: string; + }; +} + +export interface RolesByProvider { + cogntoStaticRoles: Array; + cognitoDynamicRoles: Array; + oidcStaticRoles: Array; + oidcDynamicRoles: Array; + iamRoles: Array; + apiKeyRoles: Array; + lambdaRoles: Array; +} + +export interface AuthRule { + allow: AuthStrategy; + provider?: AuthProvider; + ownerField?: string; + identityClaim?: string; + groupsField?: string; + groupClaim?: string; + groups?: string[]; + operations?: ModelOperation[]; + // Used only for IAM provider to decide if an IAM policy needs to be generated. IAM auth with AdminUI does not need IAM policies + generateIAMPolicy?: boolean; +} + +export interface RoleDefinition { + provider: AuthProvider; + strategy: AuthStrategy; + static: boolean; + claim?: string; + entity?: string; + // specific to mutations + allowedFields?: Array; + nullAllowedFields?: Array; +} + +export interface AuthDirective { + rules: AuthRule[]; +} + +export interface ConfiguredAuthProviders { + default: AuthProvider; + onlyDefaultAuthProviderConfigured: boolean; + hasApiKey: boolean; + hasUserPools: boolean; + hasOIDC: boolean; + hasIAM: boolean; + hasLambda: boolean; + hasAdminUIEnabled: boolean; + adminUserPoolID?: string; +} + +export interface AuthTransformerConfig { + addAwsIamAuthInOutputSchema: boolean; + authConfig?: AppSyncAuthConfiguration; + adminUserPoolID?: string; +} + +export const authDirectiveDefinition = ` + directive @auth(rules: [AuthRule!]!) on OBJECT | FIELD_DEFINITION + input AuthRule { + allow: AuthStrategy! + provider: AuthProvider + identityClaim: String + groupClaim: String + ownerField: String + groupsField: String + groups: [String] + operations: [ModelOperation] + } + enum AuthStrategy { + owner + groups + private + public + custom + } + enum AuthProvider { + apiKey + iam + oidc + userPools + function + } + enum ModelOperation { + create + update + delete + read + } +`; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/iam.ts b/packages/amplify-graphql-auth-transformer/src/utils/iam.ts new file mode 100644 index 00000000000..596f976ceb6 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/utils/iam.ts @@ -0,0 +1,70 @@ +import * as cdk from '@aws-cdk/core'; +interface PolicyDocument { + [key: string]: any; +} + +export const createPolicyDocumentForManagedPolicy = (resources: Set) => { + const policyDocuments = new Array(); + let policyDocumentResources = new Array(); + let resourceSize = 0; + + // 6144 bytes is the maximum policy payload size, but there is structural overhead, hence the 6000 bytes + const MAX_BUILT_SIZE_BYTES = 6000; + // The overhead is the amount of static policy arn contents like region, accountid, etc. + // arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiId}/types/${typeName}/fields/${fieldName} + // 16 15 13 5 27 6 X+1 7 Y + // 89 + 11 extra = 100 + const RESOURCE_OVERHEAD = 100; + + const createPolicyDocument = (newPolicyDocumentResources: Array): PolicyDocument => { + return { + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Action: ['appsync:GraphQL'], + Resource: newPolicyDocumentResources, + }, + ], + }; + }; + + for (const resource of resources) { + // We always have 2 parts, no need to check + const [typeName, fieldName] = resource.split('/'); + + if (fieldName !== 'null') { + policyDocumentResources.push( + cdk.Fn.sub('arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiId}/types/${typeName}/fields/${fieldName}', { + apiId: cdk.Fn.getAtt('GraphQLAPI', 'ApiId').toString(), + typeName, + fieldName, + }).toString(), + ); + resourceSize += RESOURCE_OVERHEAD + typeName.length + fieldName.length; + } else { + policyDocumentResources.push( + cdk.Fn.sub('arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiId}/types/${typeName}/*', { + apiId: cdk.Fn.getAtt('GraphQLAPI', 'ApiId').toString(), + typeName, + }).toString(), + ); + resourceSize = RESOURCE_OVERHEAD + typeName.length; + } + // + // Check size of resource and if needed create a new one and clear the resources and + // reset accumulated size + // + if (resourceSize > MAX_BUILT_SIZE_BYTES) { + const policyDocument = createPolicyDocument(policyDocumentResources.slice(0, policyDocumentResources.length - 1)); + policyDocuments.push(policyDocument); + // Remove all but the last item + policyDocumentResources = policyDocumentResources.slice(-1); + resourceSize = 0; + } + } + if (policyDocumentResources.length > 0) { + policyDocuments.push(createPolicyDocument(policyDocumentResources)); + } + return policyDocuments; +}; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/index.ts b/packages/amplify-graphql-auth-transformer/src/utils/index.ts new file mode 100644 index 00000000000..130ab65db97 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/utils/index.ts @@ -0,0 +1,159 @@ +import { ModelDirectiveConfiguration, SubscriptionLevel } from '@aws-amplify/graphql-model-transformer'; +import { DirectiveWrapper } from '@aws-amplify/graphql-transformer-core'; +import { AppSyncAuthMode } from '@aws-amplify/graphql-transformer-interfaces'; +import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { Stack } from '@aws-cdk/core'; +import { DirectiveNode, ObjectTypeDefinitionNode } from 'graphql'; +import { toCamelCase, plurality, graphqlName, toUpper } from 'graphql-transformer-common'; +import { + AuthProvider, + AuthRule, + AuthTransformerConfig, + ConfiguredAuthProviders, + RoleDefinition, + RolesByProvider, + SearchableConfig, +} from './definitions'; + +export * from './constants'; +export * from './definitions'; +export * from './validations'; +export * from './schema'; +export * from './iam'; + +export const splitRoles = (roles: Array): RolesByProvider => { + return { + cogntoStaticRoles: roles.filter(r => r.static && r.provider === 'userPools'), + cognitoDynamicRoles: roles.filter(r => !r.static && r.provider === 'userPools'), + oidcStaticRoles: roles.filter(r => r.static && r.provider === 'oidc'), + oidcDynamicRoles: roles.filter(r => !r.static && r.provider === 'oidc'), + iamRoles: roles.filter(r => r.provider === 'iam'), + apiKeyRoles: roles.filter(r => r.provider === 'apiKey'), + lambdaRoles: roles.filter(r => r.provider === 'function'), + }; +}; +/** + * Ensure the following defaults + * - provider + * - iam policy generation + */ +export const ensureAuthRuleDefaults = (rules: AuthRule[]) => { + // We assign the default provider if an override is not present make further handling easier. + for (const rule of rules) { + if (!rule.provider) { + switch (rule.allow) { + case 'owner': + case 'groups': + rule.provider = 'userPools'; + break; + case 'private': + rule.provider = 'userPools'; + break; + case 'public': + rule.provider = 'apiKey'; + break; + case 'custom': + rule.provider = 'function'; + break; + default: + throw new Error(`Need to specify an allow to assigned a provider: ${rule}`); + } + } + // by default we generate an IAM policy for every rule + if (rule.provider === 'iam' && !rule.generateIAMPolicy) { + rule.generateIAMPolicy = true; + } + } +}; + +export const getModelConfig = (directive: DirectiveNode, typeName: string, isDataStoreEnabled = false): ModelDirectiveConfiguration => { + const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive); + const options = directiveWrapped.getArguments({ + queries: { + get: toCamelCase(['get', typeName]), + list: toCamelCase(['list', plurality(typeName, true)]), + ...(isDataStoreEnabled ? { sync: toCamelCase(['sync', plurality(typeName, true)]) } : undefined), + }, + mutations: { + create: toCamelCase(['create', typeName]), + update: toCamelCase(['update', typeName]), + delete: toCamelCase(['delete', typeName]), + }, + subscriptions: { + level: SubscriptionLevel.on, + onCreate: [toCamelCase(['onCreate', typeName])], + onDelete: [toCamelCase(['onDelete', typeName])], + onUpdate: [toCamelCase(['onUpdate', typeName])], + }, + timestamps: { + createdAt: 'createdAt', + updatedAt: 'updatedAt', + }, + }); + return options; +}; + +export const getSearchableConfig = (directive: DirectiveNode, typeName: string): SearchableConfig | null => { + const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive); + const options = directiveWrapped.getArguments({ + queries: { + search: graphqlName(`search${plurality(toUpper(typeName), true)}`), + }, + }); + return options; +}; +/** + * gets stack name if the field is paired with function, predictions, or by itself + */ +export const getStackForField = ( + ctx: TransformerContextProvider, + obj: ObjectTypeDefinitionNode, + fieldName: string, + hasModelDirective: boolean, +): Stack => { + const fieldNode = obj.fields.find(f => f.name.value === fieldName); + const fieldDirectives = fieldNode.directives.map(d => d.name.value); + if (fieldDirectives.includes('function')) { + return ctx.stackManager.getStack('FunctionDirectiveStack'); + } else if (fieldDirectives.includes('predictions')) { + return ctx.stackManager.getStack('PredictionsDirectiveStack'); + } else if (hasModelDirective) { + return ctx.stackManager.getStack(obj.name.value); + } else { + return ctx.stackManager.rootStack; + } +}; + +export const getConfiguredAuthProviders = (config: AuthTransformerConfig): ConfiguredAuthProviders => { + const providers = [ + config.authConfig.defaultAuthentication.authenticationType, + ...config.authConfig.additionalAuthenticationProviders.map(p => p.authenticationType), + ]; + const getAuthProvider = (authType: AppSyncAuthMode): AuthProvider => { + switch (authType) { + case 'AMAZON_COGNITO_USER_POOLS': + return 'userPools'; + case 'API_KEY': + return 'apiKey'; + case 'AWS_IAM': + return 'iam'; + case 'OPENID_CONNECT': + return 'oidc'; + case 'AWS_LAMBDA': + return 'function'; + } + }; + const hasIAM = providers.some(p => p === 'AWS_IAM'); + const configuredProviders: ConfiguredAuthProviders = { + default: getAuthProvider(config.authConfig.defaultAuthentication.authenticationType), + onlyDefaultAuthProviderConfigured: config.authConfig.additionalAuthenticationProviders.length === 0, + hasAdminUIEnabled: hasIAM && config.addAwsIamAuthInOutputSchema, + adminUserPoolID: config.adminUserPoolID!, + hasApiKey: providers.some(p => p === 'API_KEY'), + hasUserPools: providers.some(p => p === 'AMAZON_COGNITO_USER_POOLS'), + hasOIDC: providers.some(p => p === 'OPENID_CONNECT'), + hasLambda: providers.some(p => p === 'AWS_LAMBDA'), + hasIAM, + }; + return configuredProviders; +}; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/schema.ts b/packages/amplify-graphql-auth-transformer/src/utils/schema.ts new file mode 100644 index 00000000000..d0cb99c4b0e --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/utils/schema.ts @@ -0,0 +1,209 @@ +import { ModelDirectiveConfiguration, SubscriptionLevel } from '@aws-amplify/graphql-model-transformer'; +import { + QueryFieldType, + MutationFieldType, + TransformerTransformSchemaStepContextProvider, +} from '@aws-amplify/graphql-transformer-interfaces'; +import { ObjectTypeDefinitionNode, FieldDefinitionNode, DirectiveNode, NamedTypeNode } from 'graphql'; +import { + blankObjectExtension, + extendFieldWithDirectives, + extensionWithDirectives, + isListType, + makeInputValueDefinition, + makeNamedType, + plurality, + toCamelCase, +} from 'graphql-transformer-common'; +import { RoleDefinition } from './definitions'; + +export const collectFieldNames = (object: ObjectTypeDefinitionNode): Array => { + return object.fields!.map((field: FieldDefinitionNode) => field.name.value); +}; + +export const fieldIsList = (fields: ReadonlyArray, fieldName: string) => { + return fields.some(field => field.name.value === fieldName && isListType(field.type)); +}; + +export const extendTypeWithDirectives = ( + ctx: TransformerTransformSchemaStepContextProvider, + typeName: string, + directives: Array, +): void => { + let objectTypeExtension = blankObjectExtension(typeName); + objectTypeExtension = extensionWithDirectives(objectTypeExtension, directives); + ctx.output.addObjectExtension(objectTypeExtension); +}; + +export const addDirectivesToField = ( + ctx: TransformerTransformSchemaStepContextProvider, + typeName: string, + fieldName: string, + directives: Array, +) => { + const type = ctx.output.getType(typeName) as ObjectTypeDefinitionNode; + if (type) { + const field = type.fields?.find(f => f.name.value === fieldName); + if (field) { + const newFields = [...type.fields!.filter(f => f.name.value !== field.name.value), extendFieldWithDirectives(field, directives)]; + + const newType = { + ...type, + fields: newFields, + }; + + ctx.output.putType(newType); + } + } +}; + +export const addSubscriptionArguments = ( + ctx: TransformerTransformSchemaStepContextProvider, + operationName: string, + subscriptionRoles: Array, +) => { + let subscription = ctx.output.getSubscription()!; + let createField: FieldDefinitionNode = subscription!.fields!.find(field => field.name.value === operationName) as FieldDefinitionNode; + const subcriptionArgumentList = subscriptionRoles.map(role => { + return makeInputValueDefinition(role.entity!, makeNamedType('String')); + }); + createField = { + ...createField, + arguments: subcriptionArgumentList, + }; + subscription = { + ...subscription, + fields: subscription!.fields!.map(field => (field.name.value === operationName ? createField : field)), + }; + ctx.output.putType(subscription); +}; + +export const addDirectivesToOperation = ( + ctx: TransformerTransformSchemaStepContextProvider, + typeName: string, + operationName: string, + directives: Array, +) => { + // add directives to the given operation + addDirectivesToField(ctx, typeName, operationName, directives); + + // add the directives to the result type of the operation + const type = ctx.output.getType(typeName) as ObjectTypeDefinitionNode; + if (type) { + const field = type.fields!.find(f => f.name.value === operationName); + + if (field) { + const returnFieldType = field.type as NamedTypeNode; + + if (returnFieldType.name) { + const returnTypeName = returnFieldType.name.value; + + extendTypeWithDirectives(ctx, returnTypeName, directives); + } + } + } +}; + +export const getQueryFieldNames = ( + modelDirectiveConfig: ModelDirectiveConfiguration, +): Set<{ fieldName: string; typeName: string; type: QueryFieldType }> => { + const fields: Set<{ fieldName: string; typeName: string; type: QueryFieldType }> = new Set(); + if (modelDirectiveConfig?.queries?.get) { + fields.add({ + typeName: 'Query', + fieldName: modelDirectiveConfig.queries.get, + type: QueryFieldType.GET, + }); + } + + if (modelDirectiveConfig?.queries?.list) { + fields.add({ + typeName: 'Query', + fieldName: modelDirectiveConfig.queries.list, + type: QueryFieldType.LIST, + }); + } + + if (modelDirectiveConfig?.queries?.sync) { + fields.add({ + typeName: 'Query', + fieldName: modelDirectiveConfig.queries.sync, + type: QueryFieldType.SYNC, + }); + } + return fields; +}; + +export const getMutationFieldNames = ( + modelDirectiveConfig: ModelDirectiveConfiguration, +): Set<{ fieldName: string; typeName: string; type: MutationFieldType }> => { + // Todo: get fields names from the directives + const getMutationType = (type: string): MutationFieldType => { + switch (type) { + case 'create': + return MutationFieldType.CREATE; + case 'update': + return MutationFieldType.UPDATE; + case 'delete': + return MutationFieldType.DELETE; + default: + throw new Error('Unknown mutation type'); + } + }; + + const fieldNames: Set<{ fieldName: string; typeName: string; type: MutationFieldType }> = new Set(); + for (let [mutationType, mutationName] of Object.entries(modelDirectiveConfig?.mutations || {})) { + if (mutationName) { + fieldNames.add({ + typeName: 'Mutation', + fieldName: mutationName, + type: getMutationType(mutationType), + }); + } + } + + return fieldNames; +}; + +export const getSubscriptionFieldNames = ( + modelDirectiveConfig: ModelDirectiveConfiguration, +): Set<{ + fieldName: string; + typeName: string; +}> => { + const fields: Set<{ + fieldName: string; + typeName: string; + }> = new Set(); + + if (modelDirectiveConfig?.subscriptions?.level === SubscriptionLevel.on) { + if (modelDirectiveConfig?.subscriptions?.onCreate && modelDirectiveConfig.mutations?.create) { + for (const fieldName of modelDirectiveConfig.subscriptions.onCreate) { + fields.add({ + typeName: 'Subscription', + fieldName: fieldName, + }); + } + } + + if (modelDirectiveConfig?.subscriptions?.onUpdate && modelDirectiveConfig.mutations?.update) { + for (const fieldName of modelDirectiveConfig.subscriptions.onUpdate) { + fields.add({ + typeName: 'Subscription', + fieldName: fieldName, + }); + } + } + + if (modelDirectiveConfig?.subscriptions?.onDelete && modelDirectiveConfig.mutations?.delete) { + for (const fieldName of modelDirectiveConfig.subscriptions.onDelete) { + fields.add({ + typeName: 'Subscription', + fieldName: fieldName, + }); + } + } + } + + return fields; +}; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/validations.ts b/packages/amplify-graphql-auth-transformer/src/utils/validations.ts new file mode 100644 index 00000000000..c397658c2ca --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/utils/validations.ts @@ -0,0 +1,139 @@ +import { InvalidDirectiveError } from '@aws-amplify/graphql-transformer-core'; +import { AuthRule, ConfiguredAuthProviders } from './definitions'; + +export const validateRuleAuthStrategy = (rule: AuthRule, configuredAuthProviders: ConfiguredAuthProviders) => { + // + // Groups + // + if (rule.allow === 'groups' && rule.provider !== 'userPools' && rule.provider !== 'oidc') { + throw new InvalidDirectiveError( + `@auth directive with 'groups' strategy only supports 'userPools' and 'oidc' providers, but found '${rule.provider}' assigned.`, + ); + } + if (rule.allow === 'groups' && !rule.groups && !rule.groupsField) { + throw new InvalidDirectiveError(`@auth directive with 'groups' should have a defined groups list or a groupsField.`); + } + + // + // Owner + // + if (rule.allow === 'owner') { + if (rule.provider !== null && rule.provider !== 'userPools' && rule.provider !== 'oidc') { + throw new InvalidDirectiveError( + `@auth directive with 'owner' strategy only supports 'userPools' (default) and 'oidc' providers, but \ +found '${rule.provider}' assigned.`, + ); + } + } + + // + // Public + // + if (rule.allow === 'public') { + if (rule.provider !== null && rule.provider !== 'apiKey' && rule.provider !== 'iam') { + throw new InvalidDirectiveError( + `@auth directive with 'public' strategy only supports 'apiKey' (default) and 'iam' providers, but \ +found '${rule.provider}' assigned.`, + ); + } + } + + // + // Private + // + if (rule.allow === 'private') { + if (rule.provider !== null && rule.provider !== 'userPools' && rule.provider !== 'iam') { + throw new InvalidDirectiveError( + `@auth directive with 'private' strategy only supports 'userPools' (default) and 'iam' providers, but \ +found '${rule.provider}' assigned.`, + ); + } + } + + // + // Custom + // + if (rule.allow === 'custom') { + if (rule.provider !== null && rule.provider !== 'function') { + throw new InvalidDirectiveError( + `@auth directive with 'custom' strategy only supports 'function' (default) provider, but \ +found '${rule.provider}' assigned.`, + ); + } + } + + // + // Validate provider values against project configuration. + // + if (rule.provider === 'apiKey' && configuredAuthProviders.hasApiKey === false) { + throw new InvalidDirectiveError( + `@auth directive with 'apiKey' provider found, but the project has no API Key authentication provider configured.`, + ); + } else if (rule.provider === 'oidc' && configuredAuthProviders.hasOIDC === false) { + throw new InvalidDirectiveError( + `@auth directive with 'oidc' provider found, but the project has no OPENID_CONNECT authentication provider configured.`, + ); + } else if (rule.provider === 'userPools' && configuredAuthProviders.hasUserPools === false) { + throw new InvalidDirectiveError( + `@auth directive with 'userPools' provider found, but the project has no Cognito User Pools authentication provider configured.`, + ); + } else if (rule.provider === 'iam' && configuredAuthProviders.hasIAM === false) { + throw new InvalidDirectiveError( + `@auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.`, + ); + } else if (rule.provider === 'function' && configuredAuthProviders.hasLambda === false) { + throw new InvalidDirectiveError( + `@auth directive with 'function' provider found, but the project has no Lambda authentication provider configured.`, + ); + } +}; + +export const validateRules = (rules: AuthRule[], configuredAuthProviders: ConfiguredAuthProviders) => { + for (const rule of rules) { + validateRuleAuthStrategy(rule, configuredAuthProviders); + commonRuleValidation(rule); + } +}; + +export const validateFieldRules = ( + rules: AuthRule[], + isParentTypeBuiltinType: boolean, + parentHasModelDirective: boolean, + authProviderConfig: ConfiguredAuthProviders, +) => { + for (const rule of rules) { + validateRuleAuthStrategy(rule, authProviderConfig); + + if (isParentTypeBuiltinType && rule.operations && rule.operations.length > 0) { + throw new InvalidDirectiveError( + `@auth rules on fields within Query, Mutation, Subscription cannot specify 'operations' argument as these rules \ +are already on an operation already.`, + ); + } + + if (!parentHasModelDirective && rule.operations && rule.operations.length > 0) { + throw new InvalidDirectiveError( + `@auth rules on fields within types that does not have @model directive cannot specify 'operations' argument as there are \ +operations will be generated by the CLI.`, + ); + } + + commonRuleValidation(rule); + } +}; + +// commmon rule validation between obj and field +export const commonRuleValidation = (rule: AuthRule) => { + const { identityClaim, allow, groups, groupsField, groupClaim } = rule; + if (allow === 'groups' && identityClaim) { + throw new InvalidDirectiveError(` + @auth identityClaim can only be used for 'allow: owner'`); + } + if (allow === 'owner' && groupClaim) { + throw new InvalidDirectiveError(` + @auth groupClaim can only be used 'allow: groups'`); + } + if (groupsField && groups) { + throw new InvalidDirectiveError('This rule has groupsField and groups, please use one or the other'); + } +}; diff --git a/packages/amplify-graphql-auth-transformer/tsconfig.json b/packages/amplify-graphql-auth-transformer/tsconfig.json new file mode 100644 index 00000000000..e00f8011a38 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "strict": false, // TODO enable + "rootDir": "src", + "outDir": "lib" + }, + "references": [ + {"path": "../amplify-graphql-transformer-interfaces"}, + {"path": "../amplify-graphql-transformer-core"}, + {"path": "../amplify-graphql-model-transformer"}, + {"path": "../graphql-mapping-template"}, + {"path": "../graphql-transformer-common"} + ] +} diff --git a/packages/amplify-graphql-function-transformer/CHANGELOG.md b/packages/amplify-graphql-function-transformer/CHANGELOG.md index 7ca5547a4a9..0c6e4b14de2 100644 --- a/packages/amplify-graphql-function-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-function-transformer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-function-transformer@0.4.3...@aws-amplify/graphql-function-transformer@0.5.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + ## [0.4.3](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-function-transformer@0.4.2...@aws-amplify/graphql-function-transformer@0.4.3) (2021-09-02) **Note:** Version bump only for package @aws-amplify/graphql-function-transformer diff --git a/packages/amplify-graphql-function-transformer/package.json b/packages/amplify-graphql-function-transformer/package.json index 0dad58611f0..d14b5428523 100644 --- a/packages/amplify-graphql-function-transformer/package.json +++ b/packages/amplify-graphql-function-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-function-transformer", - "version": "0.4.3", + "version": "0.5.0-graphql-vnext-dev-preview.0", "description": "Amplify GraphQL @function transformer", "repository": { "type": "git", @@ -27,13 +27,13 @@ "test": "jest" }, "dependencies": { - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/aws-lambda": "~1.124.0", "@aws-cdk/core": "~1.124.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@aws-cdk/assert": "~1.124.0" diff --git a/packages/amplify-graphql-function-transformer/src/__tests__/__snapshots__/amplify-graphql-function-transformer.test.ts.snap b/packages/amplify-graphql-function-transformer/src/__tests__/__snapshots__/amplify-graphql-function-transformer.test.ts.snap index cd54de62ac8..96ab67a114d 100644 --- a/packages/amplify-graphql-function-transformer/src/__tests__/__snapshots__/amplify-graphql-function-transformer.test.ts.snap +++ b/packages/amplify-graphql-function-transformer/src/__tests__/__snapshots__/amplify-graphql-function-transformer.test.ts.snap @@ -3,7 +3,7 @@ exports[`it generates the expected resources 1`] = ` Object { "InvokeEchofunctionLambdaDataSource.req.vtl": "## [Start] Invoke AWS Lambda data source: EchofunctionLambdaDataSource. ** -{ +$util.toJson({ \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Invoke\\", \\"payload\\": { @@ -15,7 +15,7 @@ Object { \\"request\\": $util.toJson($ctx.request), \\"prev\\": $util.toJson($ctx.prev) } -} +}) ## [End] Invoke AWS Lambda data source: EchofunctionLambdaDataSource. **", "InvokeEchofunctionLambdaDataSource.res.vtl": "## [Start] Handle error or return result. ** #if( $ctx.error ) @@ -23,11 +23,6 @@ Object { #end $util.toJson($ctx.result) ## [End] Handle error or return result. **", - "Query.echo.req.vtl": "## [Start] Stash resolver specific context.. ** -$util.qr($ctx.stash.put(\\"typeName\\", \\"Query\\")) -$util.qr($ctx.stash.put(\\"fieldName\\", \\"echo\\")) -{} -## [End] Stash resolver specific context.. **", "Query.echo.res.vtl": "$util.toJson($ctx.prev.result)", } `; diff --git a/packages/amplify-graphql-function-transformer/src/__tests__/amplify-graphql-function-transformer.test.ts b/packages/amplify-graphql-function-transformer/src/__tests__/amplify-graphql-function-transformer.test.ts index 7f0ee96fc67..4f3dc104065 100644 --- a/packages/amplify-graphql-function-transformer/src/__tests__/amplify-graphql-function-transformer.test.ts +++ b/packages/amplify-graphql-function-transformer/src/__tests__/amplify-graphql-function-transformer.test.ts @@ -116,9 +116,7 @@ test('it generates the expected resources', () => { PipelineConfig: { Functions: [{ 'Fn::GetAtt': [anything(), 'FunctionId'] }], }, - RequestMappingTemplateS3Location: { - 'Fn::Join': ['', ['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/pipelineFunctions/Query.echo.req.vtl']], - }, + RequestMappingTemplate: anything(), ResponseMappingTemplateS3Location: { 'Fn::Join': ['', ['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/pipelineFunctions/Query.echo.res.vtl']], }, diff --git a/packages/amplify-graphql-function-transformer/src/graphql-function-transformer.ts b/packages/amplify-graphql-function-transformer/src/graphql-function-transformer.ts index c6a193ddd58..2fd2e60cd5b 100644 --- a/packages/amplify-graphql-function-transformer/src/graphql-function-transformer.ts +++ b/packages/amplify-graphql-function-transformer/src/graphql-function-transformer.ts @@ -1,8 +1,15 @@ -import { DirectiveWrapper, MappingTemplate, TransformerPluginBase } from '@aws-amplify/graphql-transformer-core'; +import { + DirectiveWrapper, + IAM_AUTH_ROLE_PARAMETER, + IAM_UNAUTH_ROLE_PARAMETER, + MappingTemplate, + TransformerPluginBase, +} from '@aws-amplify/graphql-transformer-core'; import { TransformerContextProvider, TransformerSchemaVisitStepContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; import * as lambda from '@aws-cdk/aws-lambda'; +import { AuthorizationType } from '@aws-cdk/aws-appsync'; import * as cdk from '@aws-cdk/core'; -import { obj, str, ref, printBlock, compoundExpression, qref, raw, iff } from 'graphql-mapping-template'; +import { obj, str, toJson, ref, printBlock, compoundExpression, qref, raw, iff, Expression } from 'graphql-mapping-template'; import { FunctionResourceIDs, ResolverResourceIDs, ResourceConstants } from 'graphql-transformer-common'; import { DirectiveNode, ObjectTypeDefinitionNode, InterfaceTypeDefinitionNode, FieldDefinitionNode } from 'graphql'; @@ -88,19 +95,21 @@ export class FunctionTransformer extends TransformerPluginBase { functionId, MappingTemplate.s3MappingTemplateFromString( printBlock(`Invoke AWS Lambda data source: ${dataSourceId}`)( - obj({ - version: str('2018-05-29'), - operation: str('Invoke'), - payload: obj({ - typeName: ref('ctx.stash.get("typeName")'), - fieldName: ref('ctx.stash.get("fieldName")'), - arguments: ref('util.toJson($ctx.arguments)'), - identity: ref('util.toJson($ctx.identity)'), - source: ref('util.toJson($ctx.source)'), - request: ref('util.toJson($ctx.request)'), - prev: ref('util.toJson($ctx.prev)'), + toJson( + obj({ + version: str('2018-05-29'), + operation: str('Invoke'), + payload: obj({ + typeName: ref('ctx.stash.get("typeName")'), + fieldName: ref('ctx.stash.get("fieldName")'), + arguments: ref('util.toJson($ctx.arguments)'), + identity: ref('util.toJson($ctx.identity)'), + source: ref('util.toJson($ctx.source)'), + request: ref('util.toJson($ctx.request)'), + prev: ref('util.toJson($ctx.prev)'), + }), }), - }), + ), ), `${functionId}.req.vtl`, ), @@ -124,20 +133,37 @@ export class FunctionTransformer extends TransformerPluginBase { const resolverId = ResolverResourceIDs.ResolverResourceID(config.resolverTypeName, config.resolverFieldName); let resolver = createdResources.get(resolverId); + const requestTemplate: Array = [ + qref(`$ctx.stash.put("typeName", "${config.resolverTypeName}")`), + qref(`$ctx.stash.put("fieldName", "${config.resolverFieldName}")`), + ]; + const authModes = [context.authConfig.defaultAuthentication, ...(context.authConfig.additionalAuthenticationProviders || [])].map( + mode => mode?.authenticationType, + ); + if (authModes.includes(AuthorizationType.IAM)) { + const authRoleParameter = (context.stackManager.getParameter(IAM_AUTH_ROLE_PARAMETER) as cdk.CfnParameter).valueAsString; + const unauthRoleParameter = (context.stackManager.getParameter(IAM_UNAUTH_ROLE_PARAMETER) as cdk.CfnParameter).valueAsString; + requestTemplate.push( + qref( + `$ctx.stash.put("authRole", "arn:aws:sts::${ + cdk.Stack.of(context.stackManager.rootStack).account + }:assumed-role/${authRoleParameter}/CognitoIdentityCredentials")`, + ), + qref( + `$ctx.stash.put("unauthRole", "arn:aws:sts::${ + cdk.Stack.of(context.stackManager.rootStack).account + }:assumed-role/${unauthRoleParameter}/CognitoIdentityCredentials")`, + ), + ); + } + requestTemplate.push(obj({})); + if (resolver === undefined) { + // TODO: update function to use resolver manager resolver = context.api.host.addResolver( config.resolverTypeName, config.resolverFieldName, - MappingTemplate.s3MappingTemplateFromString( - printBlock('Stash resolver specific context.')( - compoundExpression([ - qref(`$ctx.stash.put("typeName", "${config.resolverTypeName}")`), - qref(`$ctx.stash.put("fieldName", "${config.resolverFieldName}")`), - obj({}), - ]), - ), - `${config.resolverTypeName}.${config.resolverFieldName}.req.vtl`, - ), + MappingTemplate.inlineTemplateFromString(printBlock('Stash resolver specific context.')(compoundExpression(requestTemplate))), MappingTemplate.s3MappingTemplateFromString( '$util.toJson($ctx.prev.result)', `${config.resolverTypeName}.${config.resolverFieldName}.res.vtl`, @@ -146,7 +172,6 @@ export class FunctionTransformer extends TransformerPluginBase { [], stack, ); - createdResources.set(resolverId, resolver); } @@ -159,7 +184,7 @@ export class FunctionTransformer extends TransformerPluginBase { function lambdaArnResource(env: cdk.CfnParameter, name: string, region?: string): string { const substitutions: { [key: string]: string } = {}; if (name.includes('${env}')) { - substitutions.env = (env as unknown) as string; + substitutions.env = env as unknown as string; } return cdk.Fn.conditionIf( ResourceConstants.CONDITIONS.HasEnvironmentParameter, diff --git a/packages/amplify-graphql-http-transformer/CHANGELOG.md b/packages/amplify-graphql-http-transformer/CHANGELOG.md index f50be5992a6..27224962a11 100644 --- a/packages/amplify-graphql-http-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-http-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.4-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-http-transformer@0.5.3...@aws-amplify/graphql-http-transformer@0.5.4-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package @aws-amplify/graphql-http-transformer + + + + + ## [0.5.3](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-http-transformer@0.5.2...@aws-amplify/graphql-http-transformer@0.5.3) (2021-09-02) **Note:** Version bump only for package @aws-amplify/graphql-http-transformer diff --git a/packages/amplify-graphql-http-transformer/package.json b/packages/amplify-graphql-http-transformer/package.json index ba9ab7a1139..72c3f465df1 100644 --- a/packages/amplify-graphql-http-transformer/package.json +++ b/packages/amplify-graphql-http-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-http-transformer", - "version": "0.5.3", + "version": "0.5.4-graphql-vnext-dev-preview.0", "description": "Amplify GraphQL @http transformer", "repository": { "type": "git", @@ -27,12 +27,12 @@ "test": "jest" }, "dependencies": { - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/core": "~1.124.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@aws-cdk/assert": "~1.124.0" diff --git a/packages/amplify-graphql-index-transformer/CHANGELOG.md b/packages/amplify-graphql-index-transformer/CHANGELOG.md index d69ff43fde1..cb97799aef5 100644 --- a/packages/amplify-graphql-index-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-index-transformer/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-index-transformer@0.3.2...@aws-amplify/graphql-index-transformer@0.4.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **amplify-provider-awscloudformation:** invoke api function from invoker plugin ([#8274](https://github.com/aws-amplify/amplify-cli/issues/8274)) ([d30f1d6](https://github.com/aws-amplify/amplify-cli/commit/d30f1d65e3b2cf259cb0382389b6ae27d914d9cc)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + ## [0.3.2](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-index-transformer@0.3.1...@aws-amplify/graphql-index-transformer@0.3.2) (2021-09-14) **Note:** Version bump only for package @aws-amplify/graphql-index-transformer diff --git a/packages/amplify-graphql-index-transformer/package.json b/packages/amplify-graphql-index-transformer/package.json index 435d2d4b266..a55011044d4 100644 --- a/packages/amplify-graphql-index-transformer/package.json +++ b/packages/amplify-graphql-index-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-index-transformer", - "version": "0.3.2", + "version": "0.4.0-graphql-vnext-dev-preview.0", "description": "Amplify GraphQL index and key transformers", "repository": { "type": "git", @@ -27,15 +27,15 @@ "test": "jest" }, "dependencies": { - "@aws-amplify/graphql-model-transformer": "0.6.2", - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-model-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/aws-appsync": "~1.124.0", "@aws-cdk/aws-dynamodb": "~1.124.0", "@aws-cdk/core": "~1.124.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@aws-cdk/assert": "~1.124.0" diff --git a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap index b7414b70b56..14389fe3089 100644 --- a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap +++ b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap @@ -13,7 +13,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -124,14 +130,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -201,13 +214,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -217,7 +231,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -373,35 +393,64 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.listByEmailKindDate.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -520,12 +569,38 @@ $util.toJson($GetRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.listByEmailKindDate.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -535,8 +610,20 @@ $util.toJson($ctx.result)", #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -560,13 +647,13 @@ $util.toJson($ctx.result)", #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.testsByCategory.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -639,12 +726,77 @@ $util.toJson($ListRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByCategory.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -661,7 +813,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -772,14 +930,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -849,13 +1014,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -865,7 +1031,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1021,35 +1193,64 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.listByEmailKindDate.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -1168,12 +1369,38 @@ $util.toJson($GetRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.listByEmailKindDate.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -1183,8 +1410,20 @@ $util.toJson($ctx.result)", #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1208,13 +1447,58 @@ $util.toJson($ctx.result)", #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -1231,7 +1515,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1342,14 +1632,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1419,13 +1716,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -1435,7 +1733,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1591,35 +1895,64 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.listByEmailKindDate.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -1738,12 +2071,38 @@ $util.toJson($GetRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.listByEmailKindDate.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -1753,8 +2112,20 @@ $util.toJson($ctx.result)", #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1778,13 +2149,13 @@ $util.toJson($ctx.result)", #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.testsByCategory.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -1857,7 +2228,27 @@ $util.toJson($ListRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByCategory.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) @@ -1895,12 +2286,77 @@ $util.toJson($ctx.result)", #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByEmail.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -2136,7 +2592,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2232,14 +2694,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2307,13 +2776,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2323,7 +2793,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2464,14 +2940,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"createdAt\\": $util.dynamodb.toDynamoDB($ctx.args.createdAt) @@ -2481,25 +2964,47 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.listByEmailKindDate.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -2618,13 +3123,39 @@ $util.toJson($GetRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.listByEmailKindDate.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -2695,8 +3226,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -2720,13 +3263,13 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.testsByCategory.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -2799,7 +3342,27 @@ $util.toJson($ListRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByCategory.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) @@ -2837,12 +3400,77 @@ $util.toJson($ctx.result)", #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByEmail.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -2859,7 +3487,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.addContentToCategory.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.addContentToCategory.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.addContentToCategory.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2970,13 +3604,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"ContentCategory\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.addContentToCategory.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.addContentToCategory.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createBlog.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -2988,6 +3623,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createBlog.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -3072,13 +3713,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Blog\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createBlog.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createBlog.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createItem.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3090,7 +3732,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createItem.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createItem.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3193,13 +3841,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Item\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createItem.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createItem.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3211,7 +3860,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3307,13 +3962,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTodo.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3325,6 +3981,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createTodo.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -3409,13 +4071,20 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Todo\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTodo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTodo.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteBlog.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -3473,14 +4142,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteBlog.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteBlog.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteContentFromCategory.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteContentFromCategory.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteContentFromCategory.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3550,14 +4226,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteContentFromCategory.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteContentFromCategory.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteItem.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteItem.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3625,14 +4308,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteItem.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteItem.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3700,13 +4390,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteTodo.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -3764,13 +4461,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTodo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTodo.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateBlog.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -3780,6 +4478,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateBlog.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -3909,13 +4613,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateBlog.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateBlog.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateItem.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -3925,7 +4630,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateItem.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateItem.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -4073,13 +4784,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateItem.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateItem.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -4089,7 +4801,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -4230,13 +4948,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTodo.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -4246,6 +4965,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateTodo.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -4375,13 +5100,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTodo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTodo.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.byCreatedAt.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") @@ -4414,35 +5140,89 @@ $util.toJson($UpdateItem) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.byCreatedAt.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Query.getBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getBlog.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getBlog.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getBlog.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getItem.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getItem.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"orderId\\": $util.dynamodb.toDynamoDB($ctx.args.orderId), \\"status#createdAt\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.status}#\${ctx.args.createdAt}\\") @@ -4452,26 +5232,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getItem.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getItem.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getItem.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"createdAt\\": $util.dynamodb.toDynamoDB($ctx.args.createdAt) @@ -4481,47 +5289,97 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTodo.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) -#else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) +#else + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTodo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTodo.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.itemsByCreatedAt.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -4594,7 +5452,27 @@ $util.toJson($GetRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.itemsByCreatedAt.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) @@ -4672,12 +5550,38 @@ $util.toJson($ctx.result)", #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.itemsByStatus.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Query.listBlogs.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listBlogs.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -4687,8 +5591,20 @@ $util.toJson($ctx.result)", #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -4712,13 +5628,13 @@ $util.toJson($ctx.result)", #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listBlogs.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listBlogs.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.listByEmailKindDate.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -4837,7 +5753,27 @@ $util.toJson($ListRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.listByEmailKindDate.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) @@ -4978,13 +5914,39 @@ $util.toJson($ctx.result)", #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.listContentByCategory.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", - "Query.listItems.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listItems.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listItems.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.orderId) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'orderId'.\\", \\"InvalidArgumentsError\\") #end @@ -5101,8 +6063,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -5126,14 +6100,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listItems.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listItems.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] ResponseTemplate. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -5204,8 +6184,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -5229,13 +6221,13 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.testsByCategory.req.vtl": "## [Start] Set query expression for key ** #set( $modelQueryExpression = {} ) ## [Start] Validate key arguments. ** @@ -5308,7 +6300,27 @@ $util.toJson($ListRequest) #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByCategory.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) @@ -5346,7 +6358,27 @@ $util.toJson($ctx.result)", #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByEmail.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) @@ -5424,11 +6456,241 @@ $util.toJson($ctx.result)", #set( $QueryRequest.scanIndexForward = true ) #end #if( $context.args.nextToken ) #set( $QueryRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) #set( $QueryRequest.filter = $util.parseJson(\\"$util.transform.toDynamoDBFilterExpression($ctx.args.filter)\\") ) #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $QueryRequest.filter = $filterExpression ) + #end +#end $util.toJson($QueryRequest)", "Query.testsByEmailByUpdatedAt.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateBlog.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateBlog.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateContentCategory.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateContentCategory.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateContentCategory.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateItem.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateItem.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTodo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTodo.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteBlog.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteBlog.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteContentCategory.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteContentCategory.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteContentCategory.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteItem.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteItem.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTodo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTodo.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateBlog.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateBlog.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateBlog.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateItem.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateItem.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateItem.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTodo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTodo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTodo.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; diff --git a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap index 327c5ae5eca..720b0ac0b31 100644 --- a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap +++ b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap @@ -13,7 +13,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -116,14 +122,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -191,13 +204,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -207,7 +221,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -355,14 +375,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"kind#other\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.kind}#\${ctx.args.other}\\") @@ -372,26 +399,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -508,8 +563,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -533,13 +600,58 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -556,7 +668,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -652,14 +770,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -727,13 +852,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -743,7 +869,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -884,14 +1016,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"kind\\": $util.dynamodb.toDynamoDB($ctx.args.kind) @@ -901,26 +1040,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -991,8 +1158,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1016,13 +1195,58 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -1039,7 +1263,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1134,14 +1364,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1208,13 +1445,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -1224,7 +1462,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1364,14 +1608,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) })) @@ -1380,26 +1631,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -1427,8 +1706,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1452,13 +1743,58 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -1475,7 +1811,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1571,14 +1913,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1646,13 +1995,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -1662,7 +2012,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1803,14 +2159,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"status\\": $util.dynamodb.toDynamoDB($ctx.args.status), \\"lastStatus\\": $util.dynamodb.toDynamoDB($ctx.args.lastStatus) @@ -1820,26 +2183,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.status) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'status'.\\", \\"InvalidArgumentsError\\") #end @@ -1910,8 +2301,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1935,13 +2338,58 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -1958,7 +2406,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2054,14 +2508,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2129,13 +2590,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.testCreate.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -2147,7 +2609,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testCreate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testCreate.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.testCreate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2243,14 +2711,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.testCreate.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.testCreate.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.testDelete.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.testDelete.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.testDelete.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2318,13 +2793,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.testDelete.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.testDelete.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.testUpdate.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2334,7 +2810,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testUpdate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testUpdate.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.testUpdate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2475,13 +2957,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.testUpdate.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.testUpdate.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2491,7 +2974,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2632,14 +3121,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) })) @@ -2648,26 +3144,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -2695,8 +3219,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -2720,14 +3256,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.testGet.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.testGet.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.testGet.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) @@ -2737,26 +3279,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.testGet.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.testGet.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.testGet.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.testList.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.testList.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.testList.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -2827,8 +3397,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -2852,13 +3434,58 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.testList.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.testList.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; @@ -2875,7 +3502,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2970,14 +3603,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3044,13 +3684,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.testCreate.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3062,7 +3703,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testCreate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testCreate.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.testCreate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3158,14 +3805,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.testCreate.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.testCreate.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.testDelete.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.testDelete.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.testDelete.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3233,13 +3887,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.testDelete.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.testDelete.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.testUpdate.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -3249,7 +3904,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testUpdate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testUpdate.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.testUpdate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3390,13 +4051,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.testUpdate.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.testUpdate.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -3406,7 +4068,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3546,14 +4214,21 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) })) @@ -3562,26 +4237,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -3609,8 +4312,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3634,14 +4349,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.testGet.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] ResponseTemplate. **", + "Query.testGet.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.testGet.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) @@ -3651,26 +4372,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.testGet.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.testGet.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.testGet.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.testList.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.testList.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.testList.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -3741,8 +4490,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3766,12 +4527,57 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.testList.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.testList.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; diff --git a/packages/amplify-graphql-index-transformer/src/resolvers.ts b/packages/amplify-graphql-index-transformer/src/resolvers.ts index 7bed95bc36b..7ac56acaeba 100644 --- a/packages/amplify-graphql-index-transformer/src/resolvers.ts +++ b/packages/amplify-graphql-index-transformer/src/resolvers.ts @@ -11,12 +11,16 @@ import { block, bool, compoundExpression, + equals, Expression, forEach, ifElse, iff, + int, + isNullOrEmpty, list, methodCall, + not, obj, print, printBlock, @@ -293,7 +297,7 @@ function setQuerySnippet(config: PrimaryKeyDirectiveConfiguration, ctx: Transfor expressions.push( set(ref(ResourceConstants.SNIPPETS.ModelQueryExpression), obj({})), - applyKeyExpressionForCompositeKey(keyNames, keyTypes, ResourceConstants.SNIPPETS.ModelQueryExpression), + applyKeyExpressionForCompositeKey(keyNames, keyTypes, ResourceConstants.SNIPPETS.ModelQueryExpression)!, ); return block(`Set query expression for key`, expressions); @@ -410,6 +414,7 @@ function makeQueryResolver(config: IndexDirectiveConfiguration, ctx: Transformer const dataSource = ctx.api.host.getDataSource(`${object.name.value}Table`); const queryTypeName = ctx.output.getQueryTypeName() as string; const table = getTable(ctx, object); + const authFilter = ref('ctx.stash.authFilter'); const requestVariable = 'QueryRequest'; assert(dataSource); @@ -440,10 +445,35 @@ function makeQueryResolver(config: IndexDirectiveConfiguration, ctx: Transformer set(ref(`${requestVariable}.scanIndexForward`), bool(true)), ), iff(ref('context.args.nextToken'), set(ref(`${requestVariable}.nextToken`), ref('context.args.nextToken')), true), + ifElse( + not(isNullOrEmpty(authFilter)), + compoundExpression([ + set(ref('filter'), authFilter), + iff( + not(isNullOrEmpty(ref('ctx.args.filter'))), + set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) })), + ), + ]), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))), + ), iff( - ref('context.args.filter'), - set(ref(`${requestVariable}.filter`), ref('util.parseJson("$util.transform.toDynamoDBFilterExpression($ctx.args.filter)")')), - true, + not(isNullOrEmpty(ref('filter'))), + compoundExpression([ + set( + ref(`filterExpression`), + methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))), + ), + iff( + not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))), + compoundExpression([ + iff( + equals(methodCall(ref('filterEpression.expressionValues.size')), int(0)), + qref(methodCall(ref('filterEpression.remove'), str('expressionValues'))), + ), + set(ref(`${requestVariable}.filter`), ref(`filterExpression`)), + ]), + ), + ]), ), raw(`$util.toJson($${requestVariable})`), ]), @@ -505,7 +535,7 @@ function addIndexToResolverSlot(resolver: TransformerResolverProvider, lines: st const res = resolver as any; res.addToSlot( - 'postAuth', + 'preAuth', MappingTemplate.s3MappingTemplateFromString( lines.join('\n') + '\n{}', `${res.typeName}.${res.fieldName}.{slotName}.{slotIndex}.req.vtl`, diff --git a/packages/amplify-graphql-index-transformer/src/schema.ts b/packages/amplify-graphql-index-transformer/src/schema.ts index 759b1d24800..3ffb35f72ce 100644 --- a/packages/amplify-graphql-index-transformer/src/schema.ts +++ b/packages/amplify-graphql-index-transformer/src/schema.ts @@ -322,6 +322,16 @@ export function ensureQueryField(config: IndexDirectiveConfiguration, ctx: Trans if (!queryField) { return; } + // add query field to metadata + const keyName = `${object.name.value}:indicies`; + let indicies: Set; + if (!ctx.metadata.has(keyName)) { + indicies = new Set([queryField]); + } else { + indicies = ctx.metadata.get>(keyName)!; + indicies.add(queryField); + } + ctx.metadata.set(keyName, indicies); const args = [createHashField(config)]; diff --git a/packages/amplify-graphql-model-transformer/CHANGELOG.md b/packages/amplify-graphql-model-transformer/CHANGELOG.md index 89f642832cd..74dd288049b 100644 --- a/packages/amplify-graphql-model-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-model-transformer/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.7.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-model-transformer@0.6.2...@aws-amplify/graphql-model-transformer@0.7.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **amplify-provider-awscloudformation:** remove sandbox mode directive from schema before transform ([#8272](https://github.com/aws-amplify/amplify-cli/issues/8272)) ([17aa0a2](https://github.com/aws-amplify/amplify-cli/commit/17aa0a2fd9356e05ff30e76b125554dbe7564e80)) +* **graphql-model-transformer:** [@model](https://github.com/model) conflict resolution ([#8035](https://github.com/aws-amplify/amplify-cli/issues/8035)) ([f3bdc4a](https://github.com/aws-amplify/amplify-cli/commit/f3bdc4ac1fcf596f634d9d2e968785e76f7b138c)) +* **graphql-model-transformer:** iam role name does not exceed 64 characters ([#8244](https://github.com/aws-amplify/amplify-cli/issues/8244)) ([812a671](https://github.com/aws-amplify/amplify-cli/commit/812a67163d6dd33160bf7ace9afd538c83a7af1a)) +* **graphql-model-transformer:** remove unnecessary warnings for resolver config per type ([#8265](https://github.com/aws-amplify/amplify-cli/issues/8265)) ([2f2f0a5](https://github.com/aws-amplify/amplify-cli/commit/2f2f0a5bea59278219c1f4ebb5276927dc5a0fbd)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* **amplify-provider-awscloudformation:** match env directive field for sandbox mode ([#3](https://github.com/aws-amplify/amplify-cli/issues/3)) ([3158549](https://github.com/aws-amplify/amplify-cli/commit/31585492fd4c358903d68d9640e9ac53e9b32eef)) + + + + + ## [0.6.2](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-model-transformer@0.6.1...@aws-amplify/graphql-model-transformer@0.6.2) (2021-09-14) diff --git a/packages/amplify-graphql-model-transformer/package.json b/packages/amplify-graphql-model-transformer/package.json index f109646be90..b5c6cfc7741 100644 --- a/packages/amplify-graphql-model-transformer/package.json +++ b/packages/amplify-graphql-model-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-model-transformer", - "version": "0.6.2", + "version": "0.7.0-graphql-vnext-dev-preview.0", "description": "Amplify graphql @model transformer", "repository": { "type": "git", @@ -28,8 +28,8 @@ "test-watch": "jest --watch" }, "dependencies": { - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/assets": "~1.124.0", "@aws-cdk/aws-applicationautoscaling": "~1.124.0", "@aws-cdk/aws-appsync": "~1.124.0", @@ -57,15 +57,15 @@ "@aws-cdk/region-info": "~1.124.0", "constructs": "^3.3.125", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", "lodash": "^4.17.21", "md5": "^2.3.0" }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/node": "^12.12.6", - "@types/md5": "^2.3.1" + "@types/md5": "^2.3.1", + "@types/node": "^12.12.6" }, "jest": { "transform": { diff --git a/packages/amplify-graphql-model-transformer/src/__tests__/__snapshots__/model-transformer.test.ts.snap b/packages/amplify-graphql-model-transformer/src/__tests__/__snapshots__/model-transformer.test.ts.snap index cedcc76d586..eacbd1bf352 100644 --- a/packages/amplify-graphql-model-transformer/src/__tests__/__snapshots__/model-transformer.test.ts.snap +++ b/packages/amplify-graphql-model-transformer/src/__tests__/__snapshots__/model-transformer.test.ts.snap @@ -583,7 +583,7 @@ input DeleteCommentInput { " `; -exports[`ModelTransformer: should generate sync resolver with ConflictHandlerType.AUTOMERGE 1`] = ` +exports[`ModelTransformer: should generate sync resolver with ConflictHandlerType.Automerge 1`] = ` Object { "Mutation.createAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) @@ -596,6 +596,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createAuthor.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -680,13 +686,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Author\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -698,6 +705,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createComment.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -782,13 +795,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Comment\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createEmail.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -800,6 +814,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createEmail.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -884,13 +904,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Email\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -902,6 +923,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -986,13 +1013,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createRequire.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -1004,6 +1032,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createRequire.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -1088,13 +1122,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Require\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -1106,6 +1141,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createTest.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -1190,13 +1231,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -1208,6 +1250,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createUser.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -1292,13 +1340,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"User\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.customCreatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -1310,6 +1359,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.customCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customCreatePost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -1394,13 +1449,20 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.customCreatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customCreatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.customDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customDeletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1458,13 +1520,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.customDeletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customDeletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.customUpdatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -1474,6 +1537,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.customUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customUpdatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -1603,13 +1672,20 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.customUpdatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customUpdatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteAuthor.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1667,13 +1743,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteComment.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1731,13 +1814,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteEmail.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1795,13 +1885,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1860,13 +1957,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) $util.qr($DeleteRequest.put(\\"_version\\", $util.defaultIfNull($ctx.args.input[\\"_version\\"], \\"0\\"))) $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteRequire.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1924,13 +2028,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteTest.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -1988,13 +2099,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteUser.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -2052,13 +2170,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2068,6 +2187,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateAuthor.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -2197,13 +2322,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2213,6 +2339,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateComment.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -2342,13 +2474,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateEmail.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2358,6 +2491,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateEmail.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -2487,13 +2626,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2503,6 +2643,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -2633,13 +2779,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateRequire.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2649,6 +2796,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateRequire.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -2778,13 +2931,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2794,6 +2948,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateTest.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -2923,13 +3083,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -2939,6 +3100,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateUser.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -3068,35 +3235,70 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.customGetPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.customGetPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.customGetPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.customGetPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.customListPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.customListPost.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3106,8 +3308,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3131,189 +3345,419 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.customListPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.customListPost.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getAuthor.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getAuthor.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getComment.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getComment.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getEmail.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getEmail.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getEntity.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getEntity.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getEntity.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getEntity.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getRequire.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getRequire.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getUser.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getUser.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.listAuthors.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listAuthors.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3323,8 +3767,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3348,13 +3804,19 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listAuthors.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listAuthors.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listComments.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listComments.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3364,8 +3826,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3389,13 +3863,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listComments.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listComments.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listEmails.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listEmails.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3405,8 +3885,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3430,13 +3922,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listEmails.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listEmails.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listPosts.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3446,8 +3944,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3471,13 +3981,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listRequires.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listRequires.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3487,8 +4003,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3512,13 +4040,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listRequires.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listRequires.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3528,8 +4062,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3553,13 +4099,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listUsers.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listUsers.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -3569,8 +4121,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -3594,19 +4158,46 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listUsers.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listUsers.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.syncPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.syncPosts.req.vtl": "## [Start] Sync Request template. ** +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end +#end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Sync\\", - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -3615,17 +4206,332 @@ null \\"nextToken\\": $util.toJson($util.defaultIfNull($ctx.args.nextToken, null)) } ## [End] Sync Request template. **", - "Query.syncPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.syncPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; -exports[`ModelTransformer: should generate sync resolver with ConflictHandlerType.LAMBDA 1`] = ` +exports[`ModelTransformer: should generate sync resolver with ConflictHandlerType.Lambda 1`] = ` Object { "Mutation.createAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) @@ -3638,6 +4544,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createAuthor.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -3722,13 +4634,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Author\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3740,6 +4653,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createComment.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -3824,13 +4743,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Comment\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createEmail.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3842,6 +4762,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createEmail.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -3926,13 +4852,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Email\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -3944,6 +4871,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -4028,13 +4961,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createRequire.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -4046,6 +4980,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createRequire.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -4130,13 +5070,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Require\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -4148,6 +5089,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createTest.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -4232,13 +5179,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -4250,6 +5198,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createUser.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -4334,13 +5288,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"User\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.customCreatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -4352,6 +5307,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.customCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customCreatePost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -4436,13 +5397,20 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.customCreatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customCreatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.customDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customDeletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -4500,13 +5468,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.customDeletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customDeletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.customUpdatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -4516,6 +5485,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.customUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customUpdatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -4645,13 +5620,20 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.customUpdatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customUpdatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteAuthor.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -4709,13 +5691,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteComment.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -4773,13 +5762,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteEmail.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -4837,13 +5833,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -4902,13 +5905,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) $util.qr($DeleteRequest.put(\\"_version\\", $util.defaultIfNull($ctx.args.input[\\"_version\\"], \\"0\\"))) $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteRequire.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -4966,13 +5976,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteTest.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -5030,13 +6047,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteUser.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -5094,13 +6118,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5110,6 +6135,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateAuthor.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -5239,13 +6270,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5255,6 +6287,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateComment.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -5384,13 +6422,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateEmail.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5400,6 +6439,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateEmail.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -5529,13 +6574,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5545,6 +6591,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -5675,13 +6727,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateRequire.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5691,6 +6744,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateRequire.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -5820,13 +6879,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5836,6 +6896,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateTest.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -5965,13 +7031,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -5981,6 +7048,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateUser.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -6110,35 +7183,70 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.customGetPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.customGetPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.customGetPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.customGetPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.customListPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.customListPost.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6148,8 +7256,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6173,189 +7293,419 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.customListPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.customListPost.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getAuthor.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getAuthor.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getComment.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getComment.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getEmail.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getEmail.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getEntity.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getEntity.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getEntity.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getEntity.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getRequire.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getRequire.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getUser.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getUser.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.listAuthors.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listAuthors.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6365,8 +7715,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6390,13 +7752,19 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listAuthors.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listAuthors.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listComments.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listComments.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6406,8 +7774,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6431,13 +7811,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listComments.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listComments.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listEmails.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listEmails.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6447,8 +7833,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6472,13 +7870,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listEmails.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listEmails.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listPosts.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6488,8 +7892,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6513,13 +7929,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listRequires.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listRequires.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6529,8 +7951,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6554,13 +7988,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listRequires.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listRequires.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6570,8 +8010,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6595,13 +8047,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listUsers.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listUsers.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -6611,8 +8069,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -6636,19 +8106,46 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listUsers.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listUsers.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.syncPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.syncPosts.req.vtl": "## [Start] Sync Request template. ** +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end +#end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Sync\\", - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -6657,17 +8154,332 @@ null \\"nextToken\\": $util.toJson($util.defaultIfNull($ctx.args.nextToken, null)) } ## [End] Sync Request template. **", - "Query.syncPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.syncPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; -exports[`ModelTransformer: should generate sync resolver with ConflictHandlerType.OPTIMISTIC 1`] = ` +exports[`ModelTransformer: should generate sync resolver with ConflictHandlerType.Optimistic 1`] = ` Object { "Mutation.createAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) @@ -6680,6 +8492,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createAuthor.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -6764,13 +8582,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Author\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -6782,6 +8601,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createComment.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -6866,13 +8691,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Comment\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createEmail.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -6884,6 +8710,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createEmail.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -6968,13 +8800,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Email\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -6986,6 +8819,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -7070,13 +8909,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createRequire.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -7088,6 +8928,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createRequire.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -7172,13 +9018,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Require\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -7190,6 +9037,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createTest.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -7274,13 +9127,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -7292,6 +9146,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createUser.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -7376,13 +9236,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"User\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.customCreatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -7394,6 +9255,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.customCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customCreatePost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -7478,13 +9345,20 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.customCreatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customCreatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.customDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customDeletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -7542,13 +9416,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.customDeletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customDeletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.customUpdatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -7558,6 +9433,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.customUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.customUpdatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -7687,13 +9568,20 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.customUpdatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.customUpdatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteAuthor.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -7751,13 +9639,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteComment.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -7815,13 +9710,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteEmail.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -7879,13 +9781,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -7944,13 +9853,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) $util.qr($DeleteRequest.put(\\"_version\\", $util.defaultIfNull($ctx.args.input[\\"_version\\"], \\"0\\"))) $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteRequire.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -8008,13 +9924,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteTest.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -8072,13 +9995,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteUser.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -8136,13 +10066,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -8152,6 +10083,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateAuthor.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -8281,13 +10218,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -8297,6 +10235,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateComment.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -8426,13 +10370,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateEmail.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -8442,6 +10387,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateEmail.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -8571,13 +10522,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateEmail.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -8587,6 +10539,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -8717,13 +10675,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateRequire.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -8733,6 +10692,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateRequire.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -8862,13 +10827,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateRequire.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -8878,6 +10844,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateTest.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -9007,13 +10979,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -9023,6 +10996,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateUser.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -9152,35 +11131,70 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.customGetPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.customGetPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.customGetPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.customGetPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.customListPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.customListPost.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9190,8 +11204,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9215,189 +11241,419 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.customListPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.customListPost.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getAuthor.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getAuthor.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getComment.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getComment.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getEmail.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getEmail.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getEmail.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getEntity.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getEntity.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getEntity.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getEntity.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getRequire.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getRequire.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getRequire.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getUser.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getUser.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.listAuthors.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listAuthors.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9407,8 +11663,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9432,13 +11700,19 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listAuthors.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listAuthors.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listComments.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listComments.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9448,8 +11722,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9473,13 +11759,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listComments.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listComments.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listEmails.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listEmails.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9489,8 +11781,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9514,13 +11818,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listEmails.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listEmails.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listPosts.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9530,8 +11840,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9555,13 +11877,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listRequires.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listRequires.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9571,8 +11899,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9596,13 +11936,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listRequires.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listRequires.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9612,8 +11958,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9637,13 +11995,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listUsers.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listUsers.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -9653,8 +12017,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -9678,19 +12054,46 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listUsers.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listUsers.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.syncPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.syncPosts.req.vtl": "## [Start] Sync Request template. ** +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end +#end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Sync\\", - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -9699,13 +12102,328 @@ null \\"nextToken\\": $util.toJson($util.defaultIfNull($ctx.args.nextToken, null)) } ## [End] Sync Request template. **", - "Query.syncPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.syncPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateEmail.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateEmail.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateEmail.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateRequire.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateRequire.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateRequire.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; diff --git a/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts b/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts index 557b0565fb5..97da336c127 100644 --- a/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts +++ b/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts @@ -881,7 +881,7 @@ describe('ModelTransformer: ', () => { validateModelSchema(parse(out.schema)); }); - it('should generate sync resolver with ConflictHandlerType.AUTOMERGE', () => { + it('should generate sync resolver with ConflictHandlerType.Automerge', () => { const validSchema = ` type Post @model { id: ID! @@ -913,7 +913,7 @@ describe('ModelTransformer: ', () => { validateModelSchema(parse(definition)); }); - it('should generate sync resolver with ConflictHandlerType.LAMBDA', () => { + it('should generate sync resolver with ConflictHandlerType.Lambda', () => { const validSchema = ` type Post @model { id: ID! @@ -950,7 +950,7 @@ describe('ModelTransformer: ', () => { validateModelSchema(parse(definition)); }); - it('should generate sync resolver with ConflictHandlerType.OPTIMISTIC', () => { + it('should generate sync resolver with ConflictHandlerType.Optimistic', () => { const validSchema = ` type Post @model { id: ID! diff --git a/packages/amplify-graphql-model-transformer/src/definitions.ts b/packages/amplify-graphql-model-transformer/src/definitions.ts index e1aafe51b0e..77d4eb31c58 100644 --- a/packages/amplify-graphql-model-transformer/src/definitions.ts +++ b/packages/amplify-graphql-model-transformer/src/definitions.ts @@ -13,4 +13,4 @@ export const BOOLEAN_FUNCTIONS = new Set(['attributeExists', 'attributeT export const ATTRIBUTE_TYPES = ['binary', 'binarySet', 'bool', 'list', 'map', 'number', 'numberSet', 'string', 'stringSet', '_null']; - +export const OPERATION_KEY = '__operation'; diff --git a/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts b/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts index 544ccc833f8..fc9bcddfda7 100644 --- a/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts +++ b/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts @@ -54,6 +54,7 @@ import { makeUpdateInputField, } from './graphql-types'; import { + generateAuthExpressionForSandboxMode, generateCreateInitSlotTemplate, generateCreateRequestTemplate, generateDefaultResponseMappingTemplate, @@ -64,7 +65,12 @@ import { generateUpdateInitSlotTemplate, generateUpdateRequestTemplate, } from './resolvers'; -import { generateGetRequestTemplate, generateListRequestTemplate, generateSyncRequestTemplate } from './resolvers/query'; +import { + generateGetRequestTemplate, + generateGetResponseTemplate, + generateListRequestTemplate, + generateSyncRequestTemplate, +} from './resolvers/query'; import { DirectiveWrapper, FieldWrapper, @@ -88,17 +94,17 @@ export type ModelDirectiveConfiguration = { list: OptionalAndNullable; sync: OptionalAndNullable; }>; - mutations: { + mutations: OptionalAndNullable<{ create: OptionalAndNullable; update: OptionalAndNullable; delete: OptionalAndNullable; - } | null; - subscriptions: { + }>; + subscriptions: OptionalAndNullable<{ onCreate: OptionalAndNullable[]; onUpdate: OptionalAndNullable[]; onDelete: OptionalAndNullable[]; - level: Partial; - } | null; + level: SubscriptionLevel; + }>; timestamps: OptionalAndNullable<{ createdAt: OptionalAndNullable; updatedAt: OptionalAndNullable; @@ -169,12 +175,13 @@ export class ModelTransformer extends TransformerModelBase implements Transforme `'${definition.name.value}' is a reserved type name and currently in use within the default schema element.`, ); } - // todo: get model configuration with default values and store it in the map const typeName = definition.name.value; + if (ctx.isProjectUsingDataStore()) { SyncUtils.validateResolverConfigForType(ctx, typeName); } + const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive); const options = directiveWrapped.getArguments({ queries: { @@ -188,7 +195,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme delete: toCamelCase(['delete', typeName]), }, subscriptions: { - level: SubscriptionLevel.public, + level: SubscriptionLevel.on, onCreate: [this.ensureValidSubscriptionName(toCamelCase(['onCreate', typeName]))], onDelete: [this.ensureValidSubscriptionName(toCamelCase(['onDelete', typeName]))], onUpdate: [this.ensureValidSubscriptionName(toCamelCase(['onUpdate', typeName]))], @@ -234,6 +241,8 @@ export class ModelTransformer extends TransformerModelBase implements Transforme this.addAutoGeneratableFields(ctx, type); if (ctx.isProjectUsingDataStore()) { + SyncUtils.validateResolverConfigForType(ctx, def!.name.value); + this.options.SyncConfig = SyncUtils.getSyncConfig(ctx, def!.name.value); this.addModelSyncFields(ctx, type); } } @@ -241,8 +250,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme generateResolvers = (context: TransformerContextProvider): void => { for (let type of this.typesWithModelDirective) { - const def = context.output.getObject(type); - + const def = context.output.getObject(type)!; // This name is used by the mock functionality. Changing this can break mock. const tableLogicalName = `${def!.name.value}Table`; const stack = context.stackManager.getStackFor(tableLogicalName, def!.name.value); @@ -265,7 +273,13 @@ export class ModelTransformer extends TransformerModelBase implements Transforme default: throw new Error('Unknown query field type'); } - + resolver.addToSlot( + 'postAuth', + MappingTemplate.s3MappingTemplateFromString( + generateAuthExpressionForSandboxMode(context), + `${query.typeName}.${query.fieldName}.{slotName}.{slotIndex}.req.vtl`, + ), + ); resolver.mapToStack(stack); context.resolvers.addResolver(query.typeName, query.fieldName, resolver); } @@ -284,11 +298,49 @@ export class ModelTransformer extends TransformerModelBase implements Transforme resolver = this.generateUpdateResolver(context, def!, mutation.typeName, mutation.fieldName); break; default: - throw new Error('Unknown query field type'); + throw new Error('Unknown mutation field type'); } + resolver.addToSlot( + 'postAuth', + MappingTemplate.s3MappingTemplateFromString( + generateAuthExpressionForSandboxMode(context), + `${mutation.typeName}.${mutation.fieldName}.{slotName}.{slotIndex}.req.vtl`, + ), + ); resolver.mapToStack(stack); context.resolvers.addResolver(mutation.typeName, mutation.fieldName, resolver); } + + const subscriptionLevel = this.modelDirectiveConfig.get(def.name.value)?.subscriptions?.level; + // in order to create subscription resolvers the level needs to be on + if (subscriptionLevel === SubscriptionLevel.on) { + const subscriptionFields = this.getSubscriptionFieldNames(context, def!); + for (let subscription of subscriptionFields.values()) { + let resolver; + switch (subscription.type) { + case SubscriptionFieldType.ON_CREATE: + resolver = this.generateOnCreateResolver(context, def, subscription.typeName, subscription.fieldName); + break; + case SubscriptionFieldType.ON_UPDATE: + resolver = this.generateOnUpdateResolver(context, def, subscription.typeName, subscription.fieldName); + break; + case SubscriptionFieldType.ON_DELETE: + resolver = this.generateOnDeleteResolver(context, def, subscription.typeName, subscription.fieldName); + break; + default: + throw new Error('Unknown subscription field type'); + } + resolver.addToSlot( + 'postAuth', + MappingTemplate.s3MappingTemplateFromString( + generateAuthExpressionForSandboxMode(context), + `${subscription.typeName}.${subscription.fieldName}.{slotName}.{slotIndex}.req.vtl`, + ), + ); + resolver.mapToStack(stack); + context.resolvers.addResolver(subscription.typeName, subscription.fieldName, resolver); + } + } } }; @@ -307,10 +359,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme fieldName, dataSource, MappingTemplate.s3MappingTemplateFromString(generateGetRequestTemplate(), `${typeName}.${fieldName}.req.vtl`), - MappingTemplate.s3MappingTemplateFromString( - generateDefaultResponseMappingTemplate(isSyncEnabled), - `${typeName}.${fieldName}.res.vtl`, - ), + MappingTemplate.s3MappingTemplateFromString(generateGetResponseTemplate(isSyncEnabled), `${typeName}.${fieldName}.res.vtl`), ); } return this.resolverMap[resolverKey]; @@ -359,7 +408,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme `${typeName}.${fieldName}.req.vtl`, ), MappingTemplate.s3MappingTemplateFromString( - generateDefaultResponseMappingTemplate(isSyncEnabled), + generateDefaultResponseMappingTemplate(isSyncEnabled, true), `${typeName}.${fieldName}.res.vtl`, ), ); @@ -391,7 +440,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme dataSource, MappingTemplate.s3MappingTemplateFromString(generateDeleteRequestTemplate(isSyncEnabled), `${typeName}.${fieldName}.req.vtl`), MappingTemplate.s3MappingTemplateFromString( - generateDefaultResponseMappingTemplate(isSyncEnabled), + generateDefaultResponseMappingTemplate(isSyncEnabled, true), `${typeName}.${fieldName}.res.vtl`, ), ); @@ -701,7 +750,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme dataSource, MappingTemplate.s3MappingTemplateFromString(generateCreateRequestTemplate(type.name.value), `${typeName}.${fieldName}.req.vtl`), MappingTemplate.s3MappingTemplateFromString( - generateDefaultResponseMappingTemplate(isSyncEnabled), + generateDefaultResponseMappingTemplate(isSyncEnabled, true), `${typeName}.${fieldName}.res.vtl`, ), ); @@ -1158,13 +1207,15 @@ export class ModelTransformer extends TransformerModelBase implements Transforme }), ); - const syncConfig = SyncUtils.getSyncConfig(context, def!.name.value); - if (syncConfig && SyncUtils.isLambdaSyncConfig(syncConfig)) { + if (this.options.SyncConfig && SyncUtils.isLambdaSyncConfig(this.options.SyncConfig)) { role.attachInlinePolicy( - SyncUtils.createSyncLambdaIAMPolicy(stack, syncConfig.LambdaConflictHandler.name, syncConfig.LambdaConflictHandler.region), + SyncUtils.createSyncLambdaIAMPolicy( + stack, + this.options.SyncConfig.LambdaConflictHandler.name, + this.options.SyncConfig.LambdaConflictHandler.region, + ), ); } - return role; } diff --git a/packages/amplify-graphql-model-transformer/src/index.ts b/packages/amplify-graphql-model-transformer/src/index.ts index d419f3ed214..10267d40d30 100644 --- a/packages/amplify-graphql-model-transformer/src/index.ts +++ b/packages/amplify-graphql-model-transformer/src/index.ts @@ -1,3 +1,4 @@ -export { ModelTransformer } from './graphql-model-transformer'; +export { ModelTransformer, ModelDirectiveConfiguration, SubscriptionLevel } from './graphql-model-transformer'; +export { OPERATION_KEY } from './definitions'; export * from './graphql-types'; export * from './resolvers'; diff --git a/packages/amplify-graphql-model-transformer/src/resolvers/common.ts b/packages/amplify-graphql-model-transformer/src/resolvers/common.ts index ed2fcd07e41..01c9059ac81 100644 --- a/packages/amplify-graphql-model-transformer/src/resolvers/common.ts +++ b/packages/amplify-graphql-model-transformer/src/resolvers/common.ts @@ -15,7 +15,13 @@ import { ifElse, printBlock, toJson, + qref, + str, + not, } from 'graphql-mapping-template'; +import { OPERATION_KEY } from '../definitions'; + +const API_KEY = 'API Key Authorization'; /** * Helper method to generate code that converts DynamoDB condition object to condition @@ -57,9 +63,11 @@ export const generateConditionSlot = (inputConditionObjectName: string, conditio /** * Generate common response template used by most of the resolvers. + * Append operation if response is coming from a mutation, this is to protect field resolver for subscriptions */ -export const generateDefaultResponseMappingTemplate = (isSyncEnabled: boolean): string => { +export const generateDefaultResponseMappingTemplate = (isSyncEnabled: boolean, mutation = false): string => { const statements: Expression[] = []; + if (mutation) statements.push(qref(methodCall(ref('ctx.result.put'), str(OPERATION_KEY), str('Mutation')))); if (isSyncEnabled) { statements.push( ifElse( @@ -74,14 +82,30 @@ export const generateDefaultResponseMappingTemplate = (isSyncEnabled: boolean): ); } - return printBlock('Get ResponseTemplate')(compoundExpression(statements)); + return printBlock('ResponseTemplate')(compoundExpression(statements)); }; /** - * Util function to gernate resolver key used to keep track of all the resolvers in memory + * Util function to generate resolver key used to keep track of all the resolvers in memory * @param typeName Name of the type * @param fieldName Name of the field */ export const generateResolverKey = (typeName: string, fieldName: string): string => { return `${typeName}.${fieldName}`; }; + +/** + * Util function to generate sandbox mode expression + * @param ctx context to get sandbox mode + */ +export const generateAuthExpressionForSandboxMode = (ctx: any): string => { + const enabled = ctx.resourceHelper.api.sandboxModeEnabled; + let exp; + + if (enabled) exp = iff(notEquals(methodCall(ref('util.authType')), str(API_KEY)), methodCall(ref('util.unauthorized'))); + else exp = methodCall(ref('util.unauthorized')); + + return printBlock(`Sandbox Mode ${enabled ? 'Enabled' : 'Disabled'}`)( + compoundExpression([iff(not(ref('ctx.stash.get("hasAuth")')), exp), toJson(obj({}))]), + ); +}; diff --git a/packages/amplify-graphql-model-transformer/src/resolvers/query.ts b/packages/amplify-graphql-model-transformer/src/resolvers/query.ts index d73bfb587be..2e9c86ead13 100644 --- a/packages/amplify-graphql-model-transformer/src/resolvers/query.ts +++ b/packages/amplify-graphql-model-transformer/src/resolvers/query.ts @@ -16,27 +16,81 @@ import { equals, bool, and, + isNullOrEmpty, + list, + forEach, nul, } from 'graphql-mapping-template'; import { ResourceConstants } from 'graphql-transformer-common'; +const authFilter = ref('ctx.stash.authFilter'); + /** * Generate get query resolver template */ export const generateGetRequestTemplate = (): string => { const statements: Expression[] = [ - set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('GetItem') })), + set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('Query') })), ifElse( ref('ctx.stash.metadata.modelObjectKey'), - set(ref('key'), ref('ctx.stash.metadata.modelObjectKey')), - compoundExpression([set(ref('key'), obj({ id: methodCall(ref('util.dynamodb.toDynamoDB'), ref('ctx.args.id')) }))]), + compoundExpression([ + set(ref('expression'), str('')), + set(ref('expressionValues'), obj({})), + forEach(ref('item'), ref('ctx.stash.metadata.modelObjectKey.entrySet()'), [ + set(ref('expression'), str('$expression$item.key = :$item.key AND ')), + qref(methodCall(ref('expressionValues.put'), str(':$item.key'), ref('item.value'))), + ]), + set(ref('expression'), methodCall(ref('expression.replaceAll'), str('AND $'), str(''))), + set(ref('query'), obj({ expression: ref('expression'), expressionValues: ref('expressionValues') })), + ]), + set( + ref('query'), + obj({ + expression: str('id = :id'), + expressionValues: obj({ + ':id': methodCall(ref('util.parseJson'), methodCall(ref('util.dynamodb.toDynamoDBJson'), ref('ctx.args.id'))), + }), + }), + ), + ), + qref(methodCall(ref('GetRequest.put'), str('query'), ref('query'))), + iff( + not(isNullOrEmpty(authFilter)), + qref( + methodCall( + ref('GetRequest.put'), + str('filter'), + methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), authFilter)), + ), + ), ), - qref(methodCall(ref('GetRequest.put'), str('key'), ref('key'))), toJson(ref('GetRequest')), ]; return printBlock('Get Request template')(compoundExpression(statements)); }; +export const generateGetResponseTemplate = (isSyncEnabled: boolean): string => { + const statements = new Array(); + if (isSyncEnabled) { + statements.push( + iff(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type'), ref('ctx.result'))), + ); + } else { + statements.push(iff(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type')))); + } + statements.push( + ifElse( + and([not(ref('ctx.result.items.isEmpty()')), equals(ref('ctx.result.scannedCount'), int(1))]), + toJson(ref('ctx.result.items[0]')), + compoundExpression([ + iff(and([ref('ctx.result.items.isEmpty()'), equals(ref('ctx.result.scannedCount'), int(1))]), ref('util.unauthorized()')), + toJson(nul()), + ]), + ), + ); + return printBlock('Get Response template')(compoundExpression(statements)); +}; + export const generateListRequestTemplate = (): string => { const requestVariable = 'ListRequest'; const modelQueryObj = 'ctx.stash.modelQueryExpression'; @@ -51,12 +105,20 @@ export const generateListRequestTemplate = (): string => { }), ), iff(ref('context.args.nextToken'), set(ref(`${requestVariable}.nextToken`), ref('context.args.nextToken'))), + ifElse( + not(isNullOrEmpty(authFilter)), + compoundExpression([ + set(ref('filter'), authFilter), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))), + ]), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))), + ), iff( - ref('context.args.filter'), + not(isNullOrEmpty(ref('filter'))), compoundExpression([ set( ref(`filterExpression`), - methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('ctx.args.filter'))), + methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))), ), iff( not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))), @@ -95,10 +157,37 @@ export const generateListRequestTemplate = (): string => { export const generateSyncRequestTemplate = (): string => { return printBlock('Sync Request template')( compoundExpression([ + ifElse( + not(isNullOrEmpty(authFilter)), + compoundExpression([ + set(ref('filter'), authFilter), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))), + ]), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))), + ), + iff( + not(isNullOrEmpty(ref('filter'))), + compoundExpression([ + set( + ref(`filterExpression`), + methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))), + ), + iff( + not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))), + compoundExpression([ + iff( + equals(methodCall(ref('filterEpression.expressionValues.size')), int(0)), + qref(methodCall(ref('filterEpression.remove'), str('expressionValues'))), + ), + set(ref('filter'), ref('filterExpression')), + ]), + ), + ]), + ), obj({ version: str('2018-05-29'), operation: str('Sync'), - filter: ifElse(ref('context.args.filter'), ref('util.transform.toDynamoDBFilterExpression($ctx.args.filter)'), nul()), + filter: ifElse(ref('filter'), ref('util.toJson($filter)'), nul()), limit: ref(`util.defaultIfNull($ctx.args.limit, ${ResourceConstants.DEFAULT_SYNC_QUERY_PAGE_LIMIT})`), lastSync: ref('util.toJson($util.defaultIfNull($ctx.args.lastSync, null))'), nextToken: ref('util.toJson($util.defaultIfNull($ctx.args.nextToken, null))'), diff --git a/packages/amplify-graphql-predictions-transformer/CHANGELOG.md b/packages/amplify-graphql-predictions-transformer/CHANGELOG.md index 8b61b63b1ed..b3b2cfce56b 100644 --- a/packages/amplify-graphql-predictions-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-predictions-transformer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-predictions-transformer@0.3.3...@aws-amplify/graphql-predictions-transformer@0.4.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + ## [0.3.3](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-predictions-transformer@0.3.2...@aws-amplify/graphql-predictions-transformer@0.3.3) (2021-09-02) **Note:** Version bump only for package @aws-amplify/graphql-predictions-transformer diff --git a/packages/amplify-graphql-predictions-transformer/package.json b/packages/amplify-graphql-predictions-transformer/package.json index 5af3216fb10..3100cf56e71 100644 --- a/packages/amplify-graphql-predictions-transformer/package.json +++ b/packages/amplify-graphql-predictions-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-predictions-transformer", - "version": "0.3.3", + "version": "0.4.0-graphql-vnext-dev-preview.0", "description": "Amplify GraphQL @predictions tranformer", "repository": { "type": "git", @@ -27,15 +27,15 @@ "test": "jest" }, "dependencies": { - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/aws-appsync": "~1.124.0", "@aws-cdk/aws-iam": "~1.124.0", "@aws-cdk/aws-lambda": "~1.124.0", "@aws-cdk/core": "~1.124.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@aws-cdk/assert": "~1.124.0", diff --git a/packages/amplify-graphql-predictions-transformer/src/graphql-predictions-transformer.ts b/packages/amplify-graphql-predictions-transformer/src/graphql-predictions-transformer.ts index bd3b5eb2885..db57b7e0e75 100644 --- a/packages/amplify-graphql-predictions-transformer/src/graphql-predictions-transformer.ts +++ b/packages/amplify-graphql-predictions-transformer/src/graphql-predictions-transformer.ts @@ -1,5 +1,12 @@ import * as path from 'path'; -import { DirectiveWrapper, InvalidDirectiveError, MappingTemplate, TransformerPluginBase } from '@aws-amplify/graphql-transformer-core'; +import { + DirectiveWrapper, + IAM_AUTH_ROLE_PARAMETER, + IAM_UNAUTH_ROLE_PARAMETER, + InvalidDirectiveError, + MappingTemplate, + TransformerPluginBase, +} from '@aws-amplify/graphql-transformer-core'; import { TransformerContextProvider, TransformerSchemaVisitStepContextProvider, @@ -50,6 +57,7 @@ import { translateTextAmzTarget, PREDICTIONS_DIRECTIVE_STACK, } from './utils/constants'; +import { AuthorizationType } from '@aws-cdk/aws-appsync'; type PredictionsDirectiveConfiguration = { actions: string[] | undefined; @@ -91,7 +99,7 @@ export class PredictionsTransformer extends TransformerPluginBase { } as PredictionsDirectiveConfiguration); if (!Array.isArray(args.actions)) { - args.actions = [(args.actions as unknown) as string]; + args.actions = [args.actions as unknown as string]; } validateActions(args.actions); @@ -318,23 +326,50 @@ function createResolver( if (referencesEnv(bucketName)) { const env = context.stackManager.getParameter(ResourceConstants.PARAMETERS.Env) as cdk.CfnParameter; - substitutions.env = (env as unknown) as string; + substitutions.env = env as unknown as string; + } + const requestTemplate = [ + cdk.Fn.conditionIf( + ResourceConstants.CONDITIONS.HasEnvironmentParameter, + cdk.Fn.sub(`$util.qr($ctx.stash.put("s3Bucket", "${bucketName}"))`, substitutions), + cdk.Fn.sub(`$util.qr($ctx.stash.put("s3Bucket", "${removeEnvReference(bucketName)}"))`, { + hash: cdk.Fn.select(3, cdk.Fn.split('-', cdk.Fn.ref('AWS::StackName'))), + }), + ) as unknown as string, + print(compoundExpression([qref('$ctx.stash.put("isList", false)')])), + ]; + // TODO: predictions should use resolver manager + const authModes = [context.authConfig.defaultAuthentication, ...(context.authConfig.additionalAuthenticationProviders || [])].map( + mode => mode?.authenticationType, + ); + if (authModes.includes(AuthorizationType.IAM)) { + const authRoleParameter = (context.stackManager.getParameter(IAM_AUTH_ROLE_PARAMETER) as cdk.CfnParameter).valueAsString; + const unauthRoleParameter = (context.stackManager.getParameter(IAM_UNAUTH_ROLE_PARAMETER) as cdk.CfnParameter).valueAsString; + requestTemplate.push( + `$util.qr($ctx.stash.put("authRole", "arn:aws:sts::${ + cdk.Stack.of(context.stackManager.rootStack).account + }:assumed-role/${authRoleParameter}/CognitoIdentityCredentials"))`, + `$util.qr($ctx.stash.put("unauthRole", "arn:aws:sts::${ + cdk.Stack.of(context.stackManager.rootStack).account + }:assumed-role/${unauthRoleParameter}/CognitoIdentityCredentials"))`, + ); } + requestTemplate.push(print(obj({}))); return context.api.host.addResolver( config.resolverTypeName, config.resolverFieldName, MappingTemplate.inlineTemplateFromString( - (cdk.Fn.join('\n', [ - (cdk.Fn.conditionIf( + cdk.Fn.join('\n', [ + cdk.Fn.conditionIf( ResourceConstants.CONDITIONS.HasEnvironmentParameter, cdk.Fn.sub(`$util.qr($ctx.stash.put("s3Bucket", "${bucketName}"))`, substitutions), cdk.Fn.sub(`$util.qr($ctx.stash.put("s3Bucket", "${removeEnvReference(bucketName)}"))`, { hash: cdk.Fn.select(3, cdk.Fn.split('-', cdk.Fn.ref('AWS::StackName'))), }), - ) as unknown) as string, + ) as unknown as string, print(compoundExpression([qref('$ctx.stash.put("isList", false)'), obj({})])), - ]) as unknown) as string, + ]) as unknown as string, ), MappingTemplate.inlineTemplateFromString( print( @@ -398,11 +433,11 @@ function removeEnvReference(value: string): string { function joinWithEnv(context: TransformerContextProvider, separator: string, listToJoin: any[]): string { const env = context.stackManager.getParameter(ResourceConstants.PARAMETERS.Env) as cdk.CfnParameter; - return (cdk.Fn.conditionIf( + return cdk.Fn.conditionIf( ResourceConstants.CONDITIONS.HasEnvironmentParameter, cdk.Fn.join(separator, [...listToJoin, env]), cdk.Fn.join(separator, listToJoin), - ) as unknown) as string; + ) as unknown as string; } function needsList(action: string, isCurrentlyList: boolean): boolean { @@ -500,14 +535,14 @@ function getStorageArn(context: TransformerContextProvider, bucketName: string): if (referencesEnv(bucketName)) { const env = context.stackManager.getParameter(ResourceConstants.PARAMETERS.Env) as cdk.CfnParameter; - substitutions.env = (env as unknown) as string; + substitutions.env = env as unknown as string; } - return (cdk.Fn.conditionIf( + return cdk.Fn.conditionIf( ResourceConstants.CONDITIONS.HasEnvironmentParameter, cdk.Fn.sub(s3ArnKey(bucketName), substitutions), cdk.Fn.sub(s3ArnKey(removeEnvReference(bucketName)), { hash: cdk.Fn.select(3, cdk.Fn.split('-', cdk.Fn.ref('AWS::StackName'))) }), - ) as unknown) as string; + ) as unknown as string; } function createActionFunction(context: TransformerContextProvider, stack: cdk.Stack, action: string, datasourceName: string) { diff --git a/packages/amplify-graphql-relational-transformer/CHANGELOG.md b/packages/amplify-graphql-relational-transformer/CHANGELOG.md index 5187458ef7f..a31962a989c 100644 --- a/packages/amplify-graphql-relational-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-relational-transformer/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-relational-transformer@0.2.1...@aws-amplify/graphql-relational-transformer@0.3.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* add [@many](https://github.com/many)ToMany directive ([#8195](https://github.com/aws-amplify/amplify-cli/issues/8195)) ([cc644eb](https://github.com/aws-amplify/amplify-cli/commit/cc644ebc4968f29ad6b3f0b42013d7ee6a142f7e)) + + + + + ## [0.2.1](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-relational-transformer@0.2.0...@aws-amplify/graphql-relational-transformer@0.2.1) (2021-09-14) **Note:** Version bump only for package @aws-amplify/graphql-relational-transformer diff --git a/packages/amplify-graphql-relational-transformer/package.json b/packages/amplify-graphql-relational-transformer/package.json index c5e0e625c36..aee19b02590 100644 --- a/packages/amplify-graphql-relational-transformer/package.json +++ b/packages/amplify-graphql-relational-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-relational-transformer", - "version": "0.2.1", + "version": "0.3.0-graphql-vnext-dev-preview.0", "description": "Amplify GraphQL relational modeling transformers", "repository": { "type": "git", @@ -27,16 +27,16 @@ "test": "jest" }, "dependencies": { - "@aws-amplify/graphql-index-transformer": "0.3.2", - "@aws-amplify/graphql-model-transformer": "0.6.2", - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-index-transformer": "0.4.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-model-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/aws-appsync": "~1.124.0", "@aws-cdk/aws-dynamodb": "~1.124.0", "@aws-cdk/core": "~1.124.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@aws-cdk/assert": "~1.124.0" diff --git a/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap b/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap index d06564b1739..bbb27712477 100644 --- a/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap +++ b/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap @@ -7639,7 +7639,7 @@ Object { "directives": Array [], "kind": "FieldDefinition", "loc": Object { - "end": 5275, + "end": 5260, "start": 5241, }, "name": Object { @@ -7653,22 +7653,22 @@ Object { "type": Object { "kind": "ListType", "loc": Object { - "end": 5275, + "end": 5260, "start": 5248, }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5274, + "end": 5259, "start": 5249, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5274, + "end": 5259, "start": 5249, }, - "value": "ModelPostEditorConnection", + "value": "PostEditor", }, }, }, @@ -7679,28 +7679,28 @@ Object { "directives": Array [], "kind": "FieldDefinition", "loc": Object { - "end": 5295, - "start": 5278, + "end": 5280, + "start": 5263, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5287, - "start": 5278, + "end": 5272, + "start": 5263, }, "value": "nextToken", }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5295, - "start": 5289, + "end": 5280, + "start": 5274, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5295, - "start": 5289, + "end": 5280, + "start": 5274, }, "value": "String", }, @@ -7710,7 +7710,7 @@ Object { "interfaces": Array [], "kind": "ObjectTypeDefinition", "loc": Object { - "end": 5297, + "end": 5282, "start": 5206, }, "name": Object { @@ -7732,28 +7732,28 @@ Object { "directives": Array [], "kind": "InputValueDefinition", "loc": Object { - "end": 5352, - "start": 5336, + "end": 5337, + "start": 5321, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5338, - "start": 5336, + "end": 5323, + "start": 5321, }, "value": "id", }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5352, - "start": 5340, + "end": 5337, + "start": 5325, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5352, - "start": 5340, + "end": 5337, + "start": 5325, }, "value": "ModelIDInput", }, @@ -7765,28 +7765,28 @@ Object { "directives": Array [], "kind": "InputValueDefinition", "loc": Object { - "end": 5375, - "start": 5355, + "end": 5360, + "start": 5340, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5361, - "start": 5355, + "end": 5346, + "start": 5340, }, "value": "postID", }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5375, - "start": 5363, + "end": 5360, + "start": 5348, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5375, - "start": 5363, + "end": 5360, + "start": 5348, }, "value": "ModelIDInput", }, @@ -7798,28 +7798,28 @@ Object { "directives": Array [], "kind": "InputValueDefinition", "loc": Object { - "end": 5400, - "start": 5378, + "end": 5385, + "start": 5363, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5386, - "start": 5378, + "end": 5371, + "start": 5363, }, "value": "editorID", }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5400, - "start": 5388, + "end": 5385, + "start": 5373, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5400, - "start": 5388, + "end": 5385, + "start": 5373, }, "value": "ModelIDInput", }, @@ -7831,34 +7831,34 @@ Object { "directives": Array [], "kind": "InputValueDefinition", "loc": Object { - "end": 5436, - "start": 5403, + "end": 5421, + "start": 5388, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5406, - "start": 5403, + "end": 5391, + "start": 5388, }, "value": "and", }, "type": Object { "kind": "ListType", "loc": Object { - "end": 5436, - "start": 5408, + "end": 5421, + "start": 5393, }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5435, - "start": 5409, + "end": 5420, + "start": 5394, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5435, - "start": 5409, + "end": 5420, + "start": 5394, }, "value": "ModelPostEditorFilterInput", }, @@ -7871,34 +7871,34 @@ Object { "directives": Array [], "kind": "InputValueDefinition", "loc": Object { - "end": 5471, - "start": 5439, + "end": 5456, + "start": 5424, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5441, - "start": 5439, + "end": 5426, + "start": 5424, }, "value": "or", }, "type": Object { "kind": "ListType", "loc": Object { - "end": 5471, - "start": 5443, + "end": 5456, + "start": 5428, }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5470, - "start": 5444, + "end": 5455, + "start": 5429, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5470, - "start": 5444, + "end": 5455, + "start": 5429, }, "value": "ModelPostEditorFilterInput", }, @@ -7911,28 +7911,28 @@ Object { "directives": Array [], "kind": "InputValueDefinition", "loc": Object { - "end": 5505, - "start": 5474, + "end": 5490, + "start": 5459, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5477, - "start": 5474, + "end": 5462, + "start": 5459, }, "value": "not", }, "type": Object { "kind": "NamedType", "loc": Object { - "end": 5505, - "start": 5479, + "end": 5490, + "start": 5464, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5505, - "start": 5479, + "end": 5490, + "start": 5464, }, "value": "ModelPostEditorFilterInput", }, @@ -7941,14 +7941,14 @@ Object { ], "kind": "InputObjectTypeDefinition", "loc": Object { - "end": 5507, - "start": 5299, + "end": 5492, + "start": 5284, }, "name": Object { "kind": "Name", "loc": Object { - "end": 5331, - "start": 5305, + "end": 5316, + "start": 5290, }, "value": "ModelPostEditorFilterInput", }, @@ -7956,7 +7956,7 @@ Object { ], "kind": "Document", "loc": Object { - "end": 5509, + "end": 5494, "start": 0, }, } @@ -8018,6 +8018,27 @@ Object { $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.childName.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -8031,8 +8052,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -8130,6 +8151,27 @@ $util.error($ctx.error.message, $ctx.error.type) ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -8143,8 +8185,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -8175,7 +8217,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createChild.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createChild.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -8271,13 +8319,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Child\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createChild.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createChild.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8289,6 +8338,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createComment.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -8373,13 +8428,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Comment\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createFriendship.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8391,6 +8447,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createFriendship.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -8475,13 +8537,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Friendship\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createFriendship.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createFriendship.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createParent.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8493,6 +8556,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createParent.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -8577,13 +8646,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Parent\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createParent.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createParent.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8595,7 +8665,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createPost.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -8690,13 +8766,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPostAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8708,6 +8785,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPostAuthor.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -8792,13 +8875,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"PostAuthor\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPostAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPostAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPostEditor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8810,6 +8894,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPostEditor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPostEditor.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -8894,13 +8984,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"PostEditor\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPostEditor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPostEditor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createPostModel.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -8912,6 +9003,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPostModel.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -8996,13 +9093,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"PostModel\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPostModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPostModel.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -9014,6 +9112,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createTest.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -9098,13 +9202,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createTest1.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -9116,7 +9221,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest1.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createTest1.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9219,13 +9330,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Test1\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createTest1.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createTest1.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -9237,7 +9349,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createUser.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createUser.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9340,13 +9458,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"User\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createUserModel.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -9358,7 +9477,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createUserModel.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.createUserModel.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9370,7 +9495,7 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { ## [End] Set the primary key. ** {}", - "Mutation.createUserModel.postAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createUserModel.preAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9481,14 +9606,21 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"UserModel\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createUserModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createUserModel.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteChild.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteChild.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9556,13 +9688,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteChild.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteChild.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteComment.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -9620,13 +9759,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteFriendship.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -9684,13 +9830,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteFriendship.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteFriendship.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteParent.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -9748,14 +9901,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteParent.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteParent.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deletePost.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9822,13 +9982,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePostAuthor.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -9886,13 +10053,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePostAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePostAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePostEditor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePostEditor.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -9950,13 +10124,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePostEditor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePostEditor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePostModel.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -10014,13 +10195,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePostModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePostModel.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteTest.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -10078,14 +10266,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteTest1.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteTest1.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10153,14 +10348,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteTest1.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteTest1.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteUser.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteUser.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10228,14 +10430,21 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Mutation.deleteUserModel.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** +## [End] ResponseTemplate. **", + "Mutation.deleteUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.deleteUserModel.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10246,7 +10455,7 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { })) ## [End] Set the primary key. ** {}", - "Mutation.deleteUserModel.postAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteUserModel.preAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10316,13 +10525,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteUserModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteUserModel.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateChild.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -10332,7 +10542,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateChild.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateChild.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10473,13 +10689,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateChild.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateChild.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateComment.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -10489,6 +10706,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateComment.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -10618,13 +10841,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateComment.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateFriendship.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -10634,6 +10858,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateFriendship.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -10763,13 +10993,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateFriendship.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateFriendship.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateParent.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -10779,6 +11010,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateParent.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -10908,13 +11145,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateParent.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateParent.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -10924,7 +11162,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updatePost.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -11064,13 +11308,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePostAuthor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11080,6 +11325,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePostAuthor.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -11209,13 +11460,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePostAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePostAuthor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePostEditor.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11225,6 +11477,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePostEditor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePostEditor.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -11354,13 +11612,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePostEditor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePostEditor.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePostModel.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11370,6 +11629,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePostModel.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -11499,13 +11764,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePostModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePostModel.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11515,6 +11781,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateTest.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -11644,13 +11916,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateTest1.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11660,7 +11933,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest1.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateTest1.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -11808,13 +12087,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateTest1.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateTest1.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateUser.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11824,7 +12104,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateUser.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateUser.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -11972,13 +12258,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateUser.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateUserModel.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -11988,7 +12275,13 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateUserModel.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Mutation.updateUserModel.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -12000,7 +12293,7 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { ## [End] Set the primary key. ** {}", - "Mutation.updateUserModel.postAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateUserModel.preAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -12156,45 +12449,74 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateUserModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateUserModel.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Parent.child.req.vtl": "#if( $util.isNull($ctx.source.childID) || $util.isNull($ctx.source.childName) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.childID, \\"___xamznone____\\")), - \\"name\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.childName, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id AND name = :name\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.childID, \\"___xamznone____\\"))), + \\":name\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.childName, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "Parent.child.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "Post.author.req.vtl": "#if( $util.isNull($ctx.source.owner) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.owner, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.owner, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "Post.author.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "Post.authors.req.vtl": "#if( $util.isNull($ctx.source.authorID) ) #set( $result = { @@ -12273,6 +12595,27 @@ $util.toJson($ctx.result) ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -12286,8 +12629,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -12323,6 +12666,27 @@ $util.error($ctx.error.message, $ctx.error.type) \\":partitionKey\\": $util.dynamodb.toDynamoDB($context.source.id) } } ) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -12336,8 +12700,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -12412,6 +12776,27 @@ $util.error($ctx.error.message, $ctx.error.type) $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.editorID.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -12425,8 +12810,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -12450,50 +12835,92 @@ $util.error($ctx.error.message, $ctx.error.type) "PostAuthor.post.req.vtl": "#if( $util.isNull($ctx.source.postID) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.postID, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.postID, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "PostAuthor.post.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "PostEditor.editor.req.vtl": "#if( $util.isNull($ctx.source.editorID) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.editorID, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.editorID, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "PostEditor.editor.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "PostEditor.post.req.vtl": "#if( $util.isNull($ctx.source.postID) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.postID, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.postID, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "PostEditor.post.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "PostModel.authors.req.vtl": "#if( $util.isNull($ctx.source.authorID) ) #set( $result = { @@ -12513,6 +12940,27 @@ $util.toJson($ctx.result) \\":sortKey\\": $util.dynamodb.toDynamoDB(\\"\${context.source.authorName}#\${context.source.authorSurname}\\") } } ) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -12526,8 +12974,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -12551,21 +12999,41 @@ $util.error($ctx.error.message, $ctx.error.type) "PostModel.singleAuthor.req.vtl": "#if( $util.isNull($ctx.source.authorID) || $util.isNull($ctx.source.authorName) || $util.isNull($ctx.source.authorSurname) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.authorID, \\"___xamznone____\\")), - \\"name#surname\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank(\\"\${ctx.source.authorName}#\${ctx.source.authorSurname}\\", \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id AND name#surname = :name#surname\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.authorID, \\"___xamznone____\\"))), + \\":name#surname\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank(\\"\${ctx.source.authorName}#\${ctx.source.authorSurname}\\", \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "PostModel.singleAuthor.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", - "Query.getChild.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getChild.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"name\\": $util.dynamodb.toDynamoDB($ctx.args.name) @@ -12575,92 +13043,204 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getChild.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getChild.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getChild.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getComment.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getComment.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getComment.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getFriendship.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getFriendship.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getFriendship.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getParent.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getParent.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getParent.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getPost.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getPost.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"title\\": $util.dynamodb.toDynamoDB($ctx.args.title) })) @@ -12669,92 +13249,204 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getPostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getPostAuthor.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPostAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPostAuthor.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getPostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getPostModel.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPostModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPostModel.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getTest1.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getTest1.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"email#name\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.email}#\${ctx.args.name}\\") @@ -12764,26 +13456,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest1.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest1.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getTest1.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", - "Query.getUser.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getUser.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"name#surname\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.name}#\${ctx.args.surname}\\") @@ -12793,26 +13513,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getUser.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUser.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getUser.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", - "Query.getUserModel.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.getUserModel.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"rollNumber\\": $util.dynamodb.toDynamoDB($ctx.args.rollNumber) @@ -12822,26 +13570,54 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getUserModel.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUserModel.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getUserModel.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.listChildren.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.listChildren.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listChildren.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -12912,8 +13688,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -12937,13 +13725,19 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listChildren.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listChildren.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listComments.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listComments.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -12953,8 +13747,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -12978,13 +13784,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listComments.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listComments.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listFriendships.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listFriendships.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -12994,8 +13806,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13019,13 +13843,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listFriendships.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listFriendships.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listParents.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listParents.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -13035,8 +13865,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13060,13 +13902,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listParents.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listParents.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listPostAuthors.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listPostAuthors.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -13076,8 +13924,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13101,13 +13961,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPostAuthors.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPostAuthors.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listPostModels.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listPostModels.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -13117,8 +13983,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13142,14 +14020,20 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPostModels.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPostModels.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.listPosts.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] ResponseTemplate. **", + "Query.listPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listPosts.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -13177,8 +14061,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13202,14 +14098,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.listTest1s.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] ResponseTemplate. **", + "Query.listTest1s.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listTest1s.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -13326,8 +14228,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13351,13 +14265,19 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTest1s.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTest1s.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listTests.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listTests.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -13367,8 +14287,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13392,14 +14324,20 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listTests.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.listUserModels.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] ResponseTemplate. **", + "Query.listUserModels.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listUserModels.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -13470,8 +14408,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13495,14 +14445,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listUserModels.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listUserModels.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", - "Query.listUsers.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] ResponseTemplate. **", + "Query.listUsers.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Query.listUsers.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -13619,8 +14575,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13644,13 +14612,553 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listUsers.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listUsers.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateChild.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateChild.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateFriendship.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateFriendship.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateParent.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateParent.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePostAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePostAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePostEditor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePostEditor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePostEditor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreatePostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePostModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePostModel.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateTest1.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest1.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateUserModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUserModel.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteChild.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteChild.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteFriendship.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteFriendship.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteParent.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteParent.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePostAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePostAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePostEditor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePostEditor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePostEditor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePostModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePostModel.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteTest1.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest1.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteUserModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUserModel.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateChild.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateChild.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateChild.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateComment.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateComment.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateFriendship.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateFriendship.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateFriendship.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateParent.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateParent.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateParent.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePostAuthor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePostAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePostAuthor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePostEditor.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePostEditor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePostEditor.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePostModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePostModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePostModel.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateTest1.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateTest1.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest1.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateUser.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUser.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateUserModel.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateUserModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUserModel.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", "Test.otherParts.req.vtl": "#if( $util.isNull($ctx.source.id) ) #set( $result = { \\"items\\": [] @@ -13728,6 +15236,27 @@ $util.toJson($ListRequest) ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -13741,8 +15270,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -13839,6 +15368,27 @@ $util.error($ctx.error.message, $ctx.error.type) ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -13852,8 +15402,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -13928,6 +15478,27 @@ $util.error($ctx.error.message, $ctx.error.type) $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.friendID.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -13941,8 +15512,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -14017,6 +15588,27 @@ $util.error($ctx.error.message, $ctx.error.type) $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.postID.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -14030,8 +15622,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -14106,6 +15698,27 @@ $util.error($ctx.error.message, $ctx.error.type) $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.postID.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -14119,8 +15732,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, diff --git a/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-many-to-many-transformer.test.ts.snap b/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-many-to-many-transformer.test.ts.snap index 3e58971d8e4..97868fefb2d 100644 --- a/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-many-to-many-transformer.test.ts.snap +++ b/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-many-to-many-transformer.test.ts.snap @@ -317,6 +317,27 @@ Object { $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.fooID.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -330,8 +351,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -406,6 +427,27 @@ $util.error($ctx.error.message, $ctx.error.type) $util.qr($query.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.barID.ge\\" })) #end ## [End] Applying Key Condition ** + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end + #else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end + #end + #if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $filter = $filterExpression ) + #end + #end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"Query\\", @@ -419,8 +461,8 @@ false #else true #end, - \\"filter\\": #if( $context.args.filter ) -$util.transform.toDynamoDBFilterExpression($ctx.args.filter) + \\"filter\\": #if( $filter ) +$util.toJson($filter) #else null #end, @@ -444,34 +486,62 @@ $util.error($ctx.error.message, $ctx.error.type) "FooBar.bar.req.vtl": "#if( $util.isNull($ctx.source.barID) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.barID, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.barID, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "FooBar.bar.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "FooBar.foo.req.vtl": "#if( $util.isNull($ctx.source.fooID) ) #return #else -{ - \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\", - \\"key\\": { - \\"id\\": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.fooID, \\"___xamznone____\\")) - } + #set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"Query\\" +} ) + $util.qr($GetRequest.put(\\"query\\", { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.fooID, \\"___xamznone____\\"))) } +})) + #if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) + #end + $util.toJson($GetRequest) #end", "FooBar.foo.res.vtl": "#if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else -$util.toJson($ctx.result) + #if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) + #else + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) + #end #end", "Mutation.createBar.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) @@ -484,6 +554,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createBar.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -568,13 +644,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Bar\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createBar.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createFoo.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -586,6 +663,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createFoo.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -670,13 +753,14 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Foo\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createFoo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createFoo.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.createFooBar.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $createdAt = $util.time.nowISO8601() ) @@ -688,6 +772,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createFooBar.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -772,13 +862,20 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"FooBar\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createFooBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createFooBar.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteBar.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -836,13 +933,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteBar.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteFoo.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -900,13 +1004,20 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteFoo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteFoo.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deleteFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deleteFooBar.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -964,13 +1075,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deleteFooBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deleteFooBar.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateBar.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -980,6 +1092,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateBar.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -1109,13 +1227,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateBar.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateFoo.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -1125,6 +1244,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateFoo.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -1254,13 +1379,14 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateFoo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateFoo.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updateFooBar.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -1270,6 +1396,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updateFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updateFooBar.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -1399,79 +1531,170 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updateFooBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updateFooBar.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getBar.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getBar.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) +#end +## [End] Get Response template. **", + "Query.getFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() #end -## [End] Get ResponseTemplate. **", +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getFoo.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getFoo.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getFoo.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.getFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getFooBar.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getFooBar.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getFooBar.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.listBars.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listBars.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -1481,8 +1704,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1506,13 +1741,19 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listBars.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listBars.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listFooBars.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listFooBars.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -1522,8 +1763,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1547,13 +1800,19 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listFooBars.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listFooBars.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.listFoos.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listFoos.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -1563,8 +1822,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -1588,12 +1859,147 @@ $util.toJson($ListRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listFoos.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listFoos.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Subscription.onCreateBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateBar.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateBar.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateFoo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateFoo.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onCreateFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreateFooBar.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateFooBar.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteBar.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteBar.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteFoo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteFoo.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeleteFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeleteFooBar.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteFooBar.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateBar.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateBar.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateFoo.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateFoo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateFoo.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdateFooBar.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdateFooBar.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateFooBar.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; diff --git a/packages/amplify-graphql-relational-transformer/src/resolvers.ts b/packages/amplify-graphql-relational-transformer/src/resolvers.ts index 97da8250ec7..1104c0bbc61 100644 --- a/packages/amplify-graphql-relational-transformer/src/resolvers.ts +++ b/packages/amplify-graphql-relational-transformer/src/resolvers.ts @@ -6,6 +6,7 @@ import { Table } from '@aws-cdk/aws-dynamodb'; import * as cdk from '@aws-cdk/core'; import { ObjectTypeDefinitionNode } from 'graphql'; import { + and, bool, compoundExpression, DynamoDBMappingTemplate, @@ -13,16 +14,22 @@ import { Expression, ifElse, iff, + int, + isNullOrEmpty, list, + methodCall, + not, nul, obj, ObjectNode, or, print, + qref, raw, ref, set, str, + toJson, } from 'graphql-mapping-template'; import { applyCompositeKeyConditionExpression, @@ -36,6 +43,8 @@ import { import { HasManyDirectiveConfiguration, HasOneDirectiveConfiguration } from './types'; import { getConnectionAttributeName } from './utils'; +const authFilter = ref('ctx.stash.authFilter'); + export function makeGetItemConnectionWithKeyResolver(config: HasOneDirectiveConfiguration, ctx: TransformerContextProvider) { const { connectionFields, field, fields, object, relatedType, relatedTypeIndex } = config; assert(relatedTypeIndex.length > 0); @@ -44,8 +53,11 @@ export function makeGetItemConnectionWithKeyResolver(config: HasOneDirectiveConf const { keySchema } = table as any; const dataSource = ctx.api.host.getDataSource(`${relatedType.name.value}Table`); const partitionKeyName = keySchema[0].attributeName; - const keyObj = { - [partitionKeyName]: ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${localFields[0]}, "${NONE_VALUE}"))`), + let totalExpressions = [`${partitionKeyName} = :${partitionKeyName}`]; + let totalExpressionValues: Record = { + [`:${partitionKeyName}`]: ref( + `util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${localFields[0]}, "${NONE_VALUE}")))`, + ), }; // Add a composite sort key or simple sort key if there is one. @@ -54,11 +66,16 @@ export function makeGetItemConnectionWithKeyResolver(config: HasOneDirectiveConf const sortKeyName = keySchema[1].attributeName; const condensedSortKeyValue = condenseRangeKey(rangeKeyFields.map(keyField => `\${ctx.source.${keyField}}`)); - keyObj[sortKeyName] = ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank("${condensedSortKeyValue}", "${NONE_VALUE}"))`); + totalExpressions.push(`${sortKeyName} = :${sortKeyName}`); + totalExpressionValues[`:${sortKeyName}`] = ref( + `util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank("${condensedSortKeyValue}", "${NONE_VALUE}")))`, + ); } else if (relatedTypeIndex.length === 2) { const sortKeyName = keySchema[1].attributeName; - - keyObj[sortKeyName] = ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${localFields[1]}, "${NONE_VALUE}"))`); + totalExpressions.push(`${sortKeyName} = :${sortKeyName}`); + totalExpressionValues[`:${sortKeyName}`] = ref( + `util.parseJson($util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${localFields[1]}, "${NONE_VALUE}")))`, + ); } const resolver = ctx.resolvers.generateQueryResolver( @@ -71,16 +88,49 @@ export function makeGetItemConnectionWithKeyResolver(config: HasOneDirectiveConf or(localFields.map(f => raw(`$util.isNull($ctx.source.${f})`))), raw('#return'), compoundExpression([ - DynamoDBMappingTemplate.getItem({ - key: obj(keyObj), - }), + set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('Query') })), + qref( + methodCall( + ref('GetRequest.put'), + str('query'), + obj({ + expression: str(totalExpressions.join(' AND ')), + expressionValues: obj(totalExpressionValues), + }), + ), + ), + iff( + not(isNullOrEmpty(authFilter)), + qref( + methodCall( + ref('GetRequest.put'), + str('filter'), + methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), authFilter)), + ), + ), + ), + toJson(ref('GetRequest')), ]), ), ), `${object.name.value}.${field.name.value}.req.vtl`, ), MappingTemplate.s3MappingTemplateFromString( - print(DynamoDBMappingTemplate.dynamoDBResponse(false)), + print( + DynamoDBMappingTemplate.dynamoDBResponse( + false, + compoundExpression([ + ifElse( + and([not(ref('ctx.result.items.isEmpty()')), equals(ref('ctx.result.scannedCount'), int(1))]), + toJson(ref('ctx.result.items[0]')), + compoundExpression([ + iff(and([ref('ctx.result.items.isEmpty()'), equals(ref('ctx.result.scannedCount'), int(1))]), ref('util.unauthorized()')), + toJson(nul()), + ]), + ), + ]), + ), + ), `${object.name.value}.${field.name.value}.res.vtl`, ), ); @@ -115,6 +165,36 @@ export function makeQueryConnectionWithKeyResolver(config: HasManyDirectiveConfi setup.push(applyCompositeKeyConditionExpression(sortKeyFieldNames, 'query', toCamelCase(sortKeyFieldNames), sortKeyFieldName)); } } + // add setup filter to query + setup.push( + ifElse( + not(isNullOrEmpty(authFilter)), + compoundExpression([ + set(ref('filter'), authFilter), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))), + ]), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))), + ), + iff( + not(isNullOrEmpty(ref('filter'))), + compoundExpression([ + set( + ref(`filterExpression`), + methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))), + ), + iff( + not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))), + compoundExpression([ + iff( + equals(methodCall(ref('filterEpression.expressionValues.size')), int(0)), + qref(methodCall(ref('filterEpression.remove'), str('expressionValues'))), + ), + set(ref('filter'), ref('filterExpression')), + ]), + ), + ]), + ), + ); const queryArguments = { query: raw('$util.toJson($query)'), @@ -123,7 +203,7 @@ export function makeQueryConnectionWithKeyResolver(config: HasManyDirectiveConfi ifElse(equals(ref('context.args.sortDirection'), str('ASC')), bool(true), bool(false)), bool(true), ), - filter: ifElse(ref('context.args.filter'), ref('util.transform.toDynamoDBFilterExpression($ctx.args.filter)'), nul()), + filter: ifElse(ref('filter'), ref('util.toJson($filter)'), nul()), limit: ref('limit'), nextToken: ifElse(ref('context.args.nextToken'), ref('util.toJson($context.args.nextToken)'), nul()), } as any; diff --git a/packages/amplify-graphql-relational-transformer/src/schema.ts b/packages/amplify-graphql-relational-transformer/src/schema.ts index db278a06b0a..905af1548cd 100644 --- a/packages/amplify-graphql-relational-transformer/src/schema.ts +++ b/packages/amplify-graphql-relational-transformer/src/schema.ts @@ -65,7 +65,7 @@ function generateModelXConnectionType( let connectionTypeExtension = blankObjectExtension(tableXConnectionName); connectionTypeExtension = extensionWithFields(connectionTypeExtension, [ - makeField('items', [], makeListType(makeNamedType(tableXConnectionName))), + makeField('items', [], makeListType(makeNamedType(relatedType.name.value))), ]); connectionTypeExtension = extensionWithFields(connectionTypeExtension, [makeField('nextToken', [], makeNamedType('String'))]); diff --git a/packages/amplify-graphql-searchable-transformer/CHANGELOG.md b/packages/amplify-graphql-searchable-transformer/CHANGELOG.md index d1733fc4a8c..ca3cd057a5d 100644 --- a/packages/amplify-graphql-searchable-transformer/CHANGELOG.md +++ b/packages/amplify-graphql-searchable-transformer/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.7.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-searchable-transformer@0.6.0...@aws-amplify/graphql-searchable-transformer@0.7.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **graphql-model-transformer:** [@model](https://github.com/model) conflict resolution ([#8035](https://github.com/aws-amplify/amplify-cli/issues/8035)) ([f3bdc4a](https://github.com/aws-amplify/amplify-cli/commit/f3bdc4ac1fcf596f634d9d2e968785e76f7b138c)) +* **graphql-model-transformer:** iam role name does not exceed 64 characters ([#8244](https://github.com/aws-amplify/amplify-cli/issues/8244)) ([812a671](https://github.com/aws-amplify/amplify-cli/commit/812a67163d6dd33160bf7ace9afd538c83a7af1a)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + # [0.6.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-searchable-transformer@0.5.1...@aws-amplify/graphql-searchable-transformer@0.6.0) (2021-09-14) diff --git a/packages/amplify-graphql-searchable-transformer/package.json b/packages/amplify-graphql-searchable-transformer/package.json index f673eb698b5..20965b2dfb5 100644 --- a/packages/amplify-graphql-searchable-transformer/package.json +++ b/packages/amplify-graphql-searchable-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-searchable-transformer", - "version": "0.6.0", + "version": "0.7.0-graphql-vnext-dev-preview.0", "description": "Amplfy GraphQL @searchable transformer", "repository": { "type": "git", @@ -27,8 +27,8 @@ "test": "jest" }, "dependencies": { - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/aws-appsync": "~1.124.0", "@aws-cdk/aws-dynamodb": "~1.124.0", "@aws-cdk/aws-ec2": "~1.124.0", @@ -37,8 +37,8 @@ "@aws-cdk/aws-lambda": "~1.124.0", "@aws-cdk/core": "~1.124.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@aws-cdk/assert": "~1.124.0", diff --git a/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap b/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap index 4f2f780d9da..49eab512b4c 100644 --- a/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap +++ b/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap @@ -604,6 +604,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.createPost.req.vtl": "## [Start] Create Request template. ** ## Begin - key condition ** #if( $ctx.stash.metadata.modelObjectKey ) @@ -688,13 +694,20 @@ $util.qr($mergedValues.put(\\"__typename\\", \\"Post\\")) #end $util.toJson($PutObject) ## [End] Create Request template. **", - "Mutation.createPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.createPost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.deletePost.req.vtl": "## [Start] Delete Request template. ** #set( $DeleteRequest = { \\"version\\": \\"2018-05-29\\", @@ -752,13 +765,14 @@ $util.qr($DeleteRequest.put(\\"key\\", $Key)) #end $util.toJson($DeleteRequest) ## [End] Delete Request template. **", - "Mutation.deletePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.deletePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Mutation.updatePost.init.1.req.vtl": "## [Start] Initialization default values. ** $util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) #set( $updatedAt = $util.time.nowISO8601() ) @@ -768,6 +782,12 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", + "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Mutation.updatePost.req.vtl": "## [Start] Mutation Update resolver. ** ## Set the default values to put request ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) @@ -897,35 +917,70 @@ $util.qr($update.put(\\"expression\\", \\"$expression\\")) #end $util.toJson($UpdateItem) ## [End] Mutation Update resolver. **", - "Mutation.updatePost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Mutation.updatePost.res.vtl": "## [Start] ResponseTemplate. ** +$util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", + "Query.getPost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.getPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.id)) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + $util.qr($GetRequest.put(\\"filter\\", $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.authFilter)))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPost.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.getPost.res.vtl": "## [Start] Get Response template. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) +#end +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", + "Query.listPosts.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", "Query.listPosts.req.vtl": "## [Start] List Request. ** #set( $limit = $util.defaultIfNull($context.args.limit, 100) ) #set( $ListRequest = { @@ -935,8 +990,20 @@ $util.toJson($GetRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = { + \\"and\\": [$filter, $ctx.args.filter] +} ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -960,13 +1027,13 @@ $util.toJson($GetRequest) #end $util.toJson($ListRequest) ## [End] List Request. **", - "Query.listPosts.res.vtl": "## [Start] Get ResponseTemplate. ** + "Query.listPosts.res.vtl": "## [Start] ResponseTemplate. ** #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end -## [End] Get ResponseTemplate. **", +## [End] ResponseTemplate. **", "Query.searchPosts.req.vtl": "#set( $indexPath = \\"/post/doc/_search\\" ) #set( $nonKeywordFields = [] ) #set( $sortValues = [] ) @@ -1006,6 +1073,25 @@ $util.toJson($ListRequest) $util.qr($aggregateValues.put(\\"$aggItem.name\\", {\\"$aggItem.type\\": {\\"field\\": \\"\${aggItem.field}.keyword\\"}})) #end #end +#if( !$util.isNullOrEmpty($ctx.stash.authFilter) ) + #set( $filter = $ctx.stash.authFilter ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"bool\\": { + \\"must\\": [$ctx.stash.authFilter, $util.transform.toElasticsearchQueryDSL($ctx.args.filter)] + } +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( $util.isNullOrEmpty($filter) ) + #set( $filter = { + \\"match_all\\": {} +} ) +#end { \\"version\\": \\"2018-05-29\\", \\"operation\\": \\"GET\\", @@ -1017,13 +1103,7 @@ $util.toJson($ListRequest) \\"size\\": #if( $context.args.limit ) $context.args.limit #else 100 #end, \\"sort\\": $sortValues, \\"version\\": false, - \\"query\\": #if( $context.args.filter ) -$util.transform.toElasticsearchQueryDSL($ctx.args.filter) -#else -{ - \\"match_all\\": {} - } -#end, + \\"query\\": $util.toJson($filter), \\"aggs\\": $util.toJson($aggregateValues) } } @@ -1059,6 +1139,51 @@ $util.toJson({ \\"nextToken\\": $nextToken, \\"aggregateItems\\": $aggregateValues })", + "Subscription.onCreatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onDeletePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", + "Subscription.onUpdatePost.postAuth.1.req.vtl": "## [Start] Sandbox Mode Disabled. ** +#if( !$ctx.stash.get(\\"hasAuth\\") ) + $util.unauthorized() +#end +$util.toJson({}) +## [End] Sandbox Mode Disabled. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Response template. ** +$util.toJson(null) +## [End] Subscription Response template. **", } `; diff --git a/packages/amplify-graphql-searchable-transformer/src/generate-resolver-vtl.ts b/packages/amplify-graphql-searchable-transformer/src/generate-resolver-vtl.ts index ccef9dc1ca9..9c93786731f 100644 --- a/packages/amplify-graphql-searchable-transformer/src/generate-resolver-vtl.ts +++ b/packages/amplify-graphql-searchable-transformer/src/generate-resolver-vtl.ts @@ -17,9 +17,13 @@ import { Expression, bool, methodCall, + isNullOrEmpty, + not, } from 'graphql-mapping-template'; import { ResourceConstants } from 'graphql-transformer-common'; +const authFilter = ref('ctx.stash.authFilter'); + export function requestTemplate(primaryKey: string, nonKeywordFields: Expression[], includeVersion: boolean = false, type: string): string { return print( compoundExpression([ @@ -64,19 +68,32 @@ export function requestTemplate(primaryKey: string, nonKeywordFields: Expression qref('$aggregateValues.put("$aggItem.name", {"$aggItem.type": {"field": "${aggItem.field}.keyword"}})'), ), ]), + ifElse( + not(isNullOrEmpty(authFilter)), + compoundExpression([ + set(ref('filter'), authFilter), + iff( + not(isNullOrEmpty(ref('ctx.args.filter'))), + set( + ref('filter'), + list([ + obj({ + bool: obj({ must: list([ref('ctx.stash.authFilter'), ref('util.transform.toElasticsearchQueryDSL($ctx.args.filter)')]) }), + }), + ]), + ), + ), + ]), + iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))), + ), + iff(isNullOrEmpty(ref('filter')), set(ref('filter'), obj({ match_all: obj({}) }))), SearchableMappingTemplate.searchTemplate({ path: str('$indexPath'), size: ifElse(ref('context.args.limit'), ref('context.args.limit'), int(ResourceConstants.DEFAULT_SEARCHABLE_PAGE_LIMIT), true), search_after: ref('util.base64Decode($context.args.nextToken)'), from: ref('context.args.from'), version: bool(includeVersion), - query: ifElse( - ref('context.args.filter'), - ref('util.transform.toElasticsearchQueryDSL($ctx.args.filter)'), - obj({ - match_all: obj({}), - }), - ), + query: methodCall(ref('util.toJson'), ref('filter')), sort: ref('sortValues'), aggs: ref('util.toJson($aggregateValues)'), }), diff --git a/packages/amplify-graphql-searchable-transformer/src/graphql-searchable-transformer.ts b/packages/amplify-graphql-searchable-transformer/src/graphql-searchable-transformer.ts index a94a93ee74e..d1832a755d3 100644 --- a/packages/amplify-graphql-searchable-transformer/src/graphql-searchable-transformer.ts +++ b/packages/amplify-graphql-searchable-transformer/src/graphql-searchable-transformer.ts @@ -138,7 +138,7 @@ export class SearchableModelTransformer extends TransformerPluginBase { MappingTemplate.s3MappingTemplateFromString(responseTemplate(false), `${typeName}.${def.fieldName}.res.vtl`), ); resolver.mapToStack(stack); - context.resolvers.addResolver(type, def.fieldName, resolver); + context.resolvers.addResolver(typeName, def.fieldName, resolver); } createStackOutputs(stack, domain.domainEndpoint, context.api.apiId, domain.domainArn); diff --git a/packages/amplify-graphql-transformer-core/CHANGELOG.md b/packages/amplify-graphql-transformer-core/CHANGELOG.md index 335419c334e..d69b0904b9a 100644 --- a/packages/amplify-graphql-transformer-core/CHANGELOG.md +++ b/packages/amplify-graphql-transformer-core/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.10.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-transformer-core@0.9.0...@aws-amplify/graphql-transformer-core@0.10.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **amplify-provider-awscloudformation:** remove sandbox mode directive from schema before transform ([#8272](https://github.com/aws-amplify/amplify-cli/issues/8272)) ([17aa0a2](https://github.com/aws-amplify/amplify-cli/commit/17aa0a2fd9356e05ff30e76b125554dbe7564e80)) +* **graphql-model-transformer:** [@model](https://github.com/model) conflict resolution ([#8035](https://github.com/aws-amplify/amplify-cli/issues/8035)) ([f3bdc4a](https://github.com/aws-amplify/amplify-cli/commit/f3bdc4ac1fcf596f634d9d2e968785e76f7b138c)) +* **graphql-model-transformer:** iam role name does not exceed 64 characters ([#8244](https://github.com/aws-amplify/amplify-cli/issues/8244)) ([812a671](https://github.com/aws-amplify/amplify-cli/commit/812a67163d6dd33160bf7ace9afd538c83a7af1a)) +* **graphql-transformer-core:** add default api name when generating stack ([#8201](https://github.com/aws-amplify/amplify-cli/issues/8201)) ([fe52f9b](https://github.com/aws-amplify/amplify-cli/commit/fe52f9b44900888b30f8ce5c88286b197e9cd3af)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* **amplify-provider-awscloudformation:** match env directive field for sandbox mode ([#3](https://github.com/aws-amplify/amplify-cli/issues/3)) ([3158549](https://github.com/aws-amplify/amplify-cli/commit/31585492fd4c358903d68d9640e9ac53e9b32eef)) + + + + + # [0.9.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-transformer-core@0.8.2...@aws-amplify/graphql-transformer-core@0.9.0) (2021-09-02) diff --git a/packages/amplify-graphql-transformer-core/package.json b/packages/amplify-graphql-transformer-core/package.json index 6864ac53933..1cd6874d7fc 100644 --- a/packages/amplify-graphql-transformer-core/package.json +++ b/packages/amplify-graphql-transformer-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-transformer-core", - "version": "0.9.0", + "version": "0.10.0-graphql-vnext-dev-preview.0", "description": "A framework to transform from GraphQL SDL to AWS CloudFormation.", "repository": { "type": "git", @@ -26,7 +26,7 @@ "clean": "rimraf ./lib" }, "dependencies": { - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/assets": "~1.124.0", "@aws-cdk/aws-applicationautoscaling": "~1.124.0", "@aws-cdk/aws-appsync": "~1.124.0", @@ -58,15 +58,15 @@ "fs-extra": "^8.1.0", "glob": "^7.1.6", "graphql": "^14.5.8", - "graphql-transformer-common": "4.19.9", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", "lodash": "^4.17.21", - "ts-dedent": "^2.0.0", - "md5": "^2.3.0" + "md5": "^2.3.0", + "ts-dedent": "^2.0.0" }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/node": "^12.12.6", - "@types/md5": "^2.3.1" + "@types/md5": "^2.3.1", + "@types/node": "^12.12.6" }, "jest": { "transform": { diff --git a/packages/amplify-graphql-transformer-core/src/config/index.ts b/packages/amplify-graphql-transformer-core/src/config/index.ts index 7cfa3497922..58b0911cbdb 100644 --- a/packages/amplify-graphql-transformer-core/src/config/index.ts +++ b/packages/amplify-graphql-transformer-core/src/config/index.ts @@ -1,21 +1,2 @@ export { TransformerProjectConfig } from './project-config'; -export { - TransformConfig, - ConflictDetectionType, - ConflictHandlerType, - ResolverConfig, - SyncConfig, - SyncConfigLambda, - SyncConfigOptimistic, - SyncConfigServer, - LambdaConflictHandler, - AppSyncAuthConfiguration, - AppSyncAuthConfigurationAPIKeyEntry, - AppSyncAuthConfigurationEntry, - AppSyncAuthConfigurationIAMEntry, - ApiKeyConfig, - AppSyncAuthConfigurationOIDCEntry, - AppSyncAuthConfigurationUserPoolEntry, - AppSyncAuthMode, - UserPoolConfig, -} from './transformer-config'; +export * from './transformer-config'; diff --git a/packages/amplify-graphql-transformer-core/src/config/transformer-config.ts b/packages/amplify-graphql-transformer-core/src/config/transformer-config.ts index c98cfad4f3d..1b044d1a36e 100644 --- a/packages/amplify-graphql-transformer-core/src/config/transformer-config.ts +++ b/packages/amplify-graphql-transformer-core/src/config/transformer-config.ts @@ -4,56 +4,13 @@ export interface TransformMigrationConfig { }; } -// Auth Config -export type AppSyncAuthMode = 'API_KEY' | 'AMAZON_COGNITO_USER_POOLS' | 'AWS_IAM' | 'OPENID_CONNECT'; -export type AppSyncAuthConfiguration = { - defaultAuthentication: AppSyncAuthConfigurationEntry; - additionalAuthenticationProviders: Array; -}; - -export type AppSyncAuthConfigurationEntry = - | AppSyncAuthConfigurationUserPoolEntry - | AppSyncAuthConfigurationAPIKeyEntry - | AppSyncAuthConfigurationIAMEntry - | AppSyncAuthConfigurationOIDCEntry; -export type AppSyncAuthConfigurationAPIKeyEntry = { - authenticationType: 'API_KEY'; - apiKeyConfig: ApiKeyConfig; -}; -export type AppSyncAuthConfigurationUserPoolEntry = { - authenticationType: 'AMAZON_COGNITO_USER_POOLS'; - userPoolConfig: UserPoolConfig; -}; -export type AppSyncAuthConfigurationIAMEntry = { - authenticationType: 'AWS_IAM'; -}; - -export type AppSyncAuthConfigurationOIDCEntry = { - authenticationType: 'OPENID_CONNECT'; - openIDConnectConfig: OpenIDConnectConfig; -}; - -export type ApiKeyConfig = { - description?: string; - apiKeyExpirationDays: number; -}; -export type UserPoolConfig = { - userPoolId: string; -}; -export type OpenIDConnectConfig = { - name: string; - issuerUrl: string; - clientId?: string; - iatTTL?: number; - authTTL?: number; -}; - // Sync Config export const enum ConflictHandlerType { OPTIMISTIC = 'OPTIMISTIC_CONCURRENCY', AUTOMERGE = 'AUTOMERGE', LAMBDA = 'LAMBDA', } + export type ConflictDetectionType = 'VERSION' | 'NONE'; export type SyncConfigOptimistic = { ConflictDetection: ConflictDetectionType; @@ -79,10 +36,6 @@ export type ResolverConfig = { project?: SyncConfig; models?: Record; }; -/** - * The transform config is specified in transform.conf.json within an Amplify - * API project directory. - */ export interface TransformConfig { /** * The transform library uses a "StackMapping" to determine which stack @@ -95,7 +48,9 @@ export interface TransformConfig { * overrides to get specific behavior out of the transformer. Users may * override the default stack mapping to customize behavior. */ - StackMapping?: Record; + StackMapping?: { + [resourceId: string]: string; + }; /** * Provide build time options to GraphQL Transformer constructor functions. @@ -103,9 +58,10 @@ export interface TransformConfig { * need to be set at build time. E.G. DeletionPolicies cannot depend on parameters. */ TransformerOptions?: { - [transformer: string]: Record; + [transformer: string]: { + [option: string]: any; + }; }; - /** * Object which states info about a resolver's configuration * Such as sync configuration for appsync local support diff --git a/packages/amplify-graphql-transformer-core/src/graphql-api.ts b/packages/amplify-graphql-transformer-core/src/graphql-api.ts index 71d9fa52dc8..631cc1f426e 100644 --- a/packages/amplify-graphql-transformer-core/src/graphql-api.ts +++ b/packages/amplify-graphql-transformer-core/src/graphql-api.ts @@ -17,6 +17,7 @@ import { Grant, IGrantable, ManagedPolicy, Role, ServicePrincipal } from '@aws-c import { CfnResource, Construct, Duration, Stack } from '@aws-cdk/core'; import { TransformerSchema } from './cdk-compat/schema-asset'; import { DefaultTransformHost } from './transform-host'; +import * as cdk from '@aws-cdk/core'; export interface GraphqlApiProps { /** @@ -113,6 +114,8 @@ export class IamResource implements APIIAMResourceProvider { export type TransformerAPIProps = GraphqlApiProps & { readonly createApiKey?: boolean; readonly host?: TransformHostProvider; + readonly sandboxModeEnabled?: boolean; + readonly environmentName?: string; }; export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { /** @@ -125,7 +128,7 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { * The TransformHost object provides resource creation utilities in AWS * such as a LambdaDataSource or a DynamoDBDataSource */ - public readonly host: TransformHostProvider + public readonly host: TransformHostProvider; /** * the ARN of the API @@ -161,10 +164,20 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { */ public readonly apiKey?: string; + /** + * Global Sandbox Mode for GraphQL API + */ + public readonly sandboxModeEnabled?: boolean; + + /** + * the amplify environment name + */ + public readonly environmentName?: string; + private schemaResource: CfnGraphQLSchema; private api: CfnGraphQLApi; private apiKeyResource?: CfnApiKey; - private authorizationConfig?: Required; + private authorizationConfig?: Required; constructor(scope: Construct, id: string, props: TransformerAPIProps) { super(scope, id); @@ -178,7 +191,7 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { const modes = [defaultMode, ...additionalModes]; this.modes = modes.map(mode => mode.authorizationType); - + this.environmentName = props.environmentName; this.validateAuthorizationProps(modes); this.api = new CfnGraphQLApi(this, 'Resource', { @@ -187,6 +200,7 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { logConfig: this.setupLogConfig(props.logConfig), openIdConnectConfig: this.setupOpenIdConnectConfig(defaultMode.openIdConnectConfig), userPoolConfig: this.setupUserPoolConfig(defaultMode.userPoolConfig), + lambdaAuthorizerConfig: this.setupLambdaConfig(defaultMode.lambdaAuthorizerConfig), additionalAuthenticationProviders: this.setupAdditionalAuthorizationModes(additionalModes), xrayEnabled: props.xrayEnabled, }); @@ -198,7 +212,9 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { this.schema = props.schema ?? new TransformerSchema(); this.schemaResource = this.schema.bind(this); - if (props.createApiKey && modes.some(mode => mode.authorizationType === AuthorizationType.API_KEY)) { + const hasApiKey = modes.some(mode => mode.authorizationType === AuthorizationType.API_KEY); + + if (props.createApiKey && hasApiKey) { const config = modes.find((mode: AuthorizationMode) => { return mode.authorizationType === AuthorizationType.API_KEY && mode.apiKeyConfig; })?.apiKeyConfig; @@ -207,13 +223,14 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { this.apiKey = this.apiKeyResource.attrApiKey; } + if (hasApiKey && props.sandboxModeEnabled) this.sandboxModeEnabled = true; + if (props.host) { this.host = props.host; this.host.setAPI(this); - } - else { + } else { this.host = new DefaultTransformHost({ - api: this + api: this, }); } } @@ -329,7 +346,22 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { }; } - private setupAdditionalAuthorizationModes(modes?: AuthorizationMode[]) { + private setupLambdaConfig(config?: any) { + if (!config) return undefined; + return { + authorizerUri: this.lambdaArnKey(config.lambdaFunction), + authorizerResultTtlInSeconds: config.ttlSeconds, + identityValidationExpression: "", + }; + } + + private lambdaArnKey(name: string) { + return this.environmentName ? + `arn:${cdk.Aws.PARTITION}:lambda:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:function:${name}-${this.environmentName}` + : `arn:${cdk.Aws.PARTITION}:lambda:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:function:${name}`; + } + + private setupAdditionalAuthorizationModes(modes?: Array) { if (!modes || modes.length === 0) return undefined; return modes.reduce( (acc, mode) => [ @@ -338,6 +370,7 @@ export class GraphQLApi extends GraphqlApiBase implements GraphQLAPIProvider { authenticationType: mode.authorizationType, userPoolConfig: this.setupUserPoolConfig(mode.userPoolConfig), openIdConnectConfig: this.setupOpenIdConnectConfig(mode.openIdConnectConfig), + lambdaAuthorizerConfig: this.setupLambdaConfig(mode.lambdaAuthorizerConfig), }, ], [], diff --git a/packages/amplify-graphql-transformer-core/src/index.ts b/packages/amplify-graphql-transformer-core/src/index.ts index 6ddfd7b974a..3ab2126bd4f 100644 --- a/packages/amplify-graphql-transformer-core/src/index.ts +++ b/packages/amplify-graphql-transformer-core/src/index.ts @@ -8,25 +8,27 @@ export { ConflictHandlerType, ResolverConfig, SyncConfig, - SyncConfigLambda, SyncConfigOptimistic, SyncConfigServer, + SyncConfigLambda, TransformConfig, TransformerProjectConfig, - AppSyncAuthConfiguration, - AppSyncAuthConfigurationAPIKeyEntry, - AppSyncAuthConfigurationEntry, - AppSyncAuthConfigurationIAMEntry, - ApiKeyConfig, - AppSyncAuthConfigurationOIDCEntry, - AppSyncAuthConfigurationUserPoolEntry, - AppSyncAuthMode, - UserPoolConfig, - LambdaConflictHandler, } from './config/index'; -export { collectDirectives, collectDirectivesByTypeNames, DirectiveWrapper } from './utils'; +export { + collectDirectives, + collectDirectivesByTypeNames, + DirectiveWrapper, + IAM_AUTH_ROLE_PARAMETER, + IAM_UNAUTH_ROLE_PARAMETER, +} from './utils'; export * from './errors'; -export { TransformerModelBase, TransformerModelEnhancerBase, TransformerPluginBase } from './transformation/transformer-plugin-base'; +export { + TransformerModelBase, + TransformerModelEnhancerBase, + TransformerPluginBase, + TransformerAuthBase, +} from './transformation/transformer-plugin-base'; +export { TransformerResolver } from './transformer-context'; /** * Returns the extra set of directives that are supported by AppSync service */ diff --git a/packages/amplify-graphql-transformer-core/src/transform-host.ts b/packages/amplify-graphql-transformer-core/src/transform-host.ts index a72f94cf87d..5ede7441a0a 100644 --- a/packages/amplify-graphql-transformer-core/src/transform-host.ts +++ b/packages/amplify-graphql-transformer-core/src/transform-host.ts @@ -26,6 +26,7 @@ export interface DefaultTransformHostOptions { export class DefaultTransformHost implements TransformHostProvider { private dataSources: Map = new Map(); + private resolvers: Map = new Map(); private api: GraphQLApi; public constructor(options: DefaultTransformHostOptions) { @@ -45,6 +46,16 @@ export class DefaultTransformHost implements TransformHostProvider { } }; + public hasResolver = (typeName: string, fieldName: string) => { + return this.resolvers.has(`${typeName}:${fieldName}`); + }; + + public getResolver = (typeName: string, fieldName: string): CfnResolver | void => { + if (this.resolvers.has(`${typeName}:${fieldName}`)) { + return this.resolvers.get(`${typeName}:${fieldName}`); + } + }; + addSearchableDataSource( name: string, awsRegion: string, @@ -125,7 +136,7 @@ export class DefaultTransformHost implements TransformHostProvider { dataSourceName?: string, pipelineConfig?: string[], stack?: Stack, - ) { + ): CfnResolver { if (dataSourceName && !Token.isUnresolved(dataSourceName) && !this.dataSources.has(dataSourceName)) { throw new Error(`DataSource ${dataSourceName} is missing in the API`); } @@ -168,6 +179,7 @@ export class DefaultTransformHost implements TransformHostProvider { }, }); this.api.addSchemaDependency(resolver); + this.resolvers.set(`${typeName}:${fieldName}`, resolver); return resolver; } else { throw new Error('Resolver needs either dataSourceName or pipelineConfig to be passed'); diff --git a/packages/amplify-graphql-transformer-core/src/transformation/sync-utils.ts b/packages/amplify-graphql-transformer-core/src/transformation/sync-utils.ts index 7d207ffa4e2..5818000f4c0 100644 --- a/packages/amplify-graphql-transformer-core/src/transformation/sync-utils.ts +++ b/packages/amplify-graphql-transformer-core/src/transformation/sync-utils.ts @@ -90,7 +90,7 @@ export function syncDataSourceConfig(): DeltaSyncConfig { export function validateResolverConfigForType(ctx: TransformerSchemaVisitStepContextProvider, typeName: string): void { const resolverConfig = ctx.getResolverConfig(); const typeResolverConfig = resolverConfig?.models?.[typeName]; - if (!typeResolverConfig || !typeResolverConfig.ConflictDetection || !typeResolverConfig.ConflictHandler) { + if (typeResolverConfig && !typeResolverConfig.ConflictDetection && !typeResolverConfig.ConflictHandler) { console.warn(`Invalid resolverConfig for type ${typeName}. Using the project resolverConfig instead.`); } } diff --git a/packages/amplify-graphql-transformer-core/src/transformation/transform.ts b/packages/amplify-graphql-transformer-core/src/transformation/transform.ts index 2571efec502..ce1fe2dde41 100644 --- a/packages/amplify-graphql-transformer-core/src/transformation/transform.ts +++ b/packages/amplify-graphql-transformer-core/src/transformation/transform.ts @@ -4,6 +4,7 @@ import { GraphQLAPIProvider, TransformerPluginProvider, TransformHostProvider, + AppSyncAuthConfiguration, } from '@aws-amplify/graphql-transformer-interfaces'; import { AuthorizationMode, AuthorizationType } from '@aws-cdk/aws-appsync'; import { App, Aws, CfnOutput, Fn } from '@aws-cdk/core'; @@ -23,13 +24,13 @@ import { TypeExtensionNode, UnionTypeDefinitionNode, } from 'graphql'; -import { AppSyncAuthConfiguration, TransformConfig } from '../config/transformer-config'; import { InvalidTransformerError, SchemaValidationError, UnknownDirectiveError } from '../errors'; import { GraphQLApi } from '../graphql-api'; import { TransformerContext } from '../transformer-context'; import { TransformerOutput } from '../transformer-context/output'; import { StackManager } from '../transformer-context/stack-manager'; -import { adoptAuthModes } from '../utils/authType'; +import { adoptAuthModes, IAM_AUTH_ROLE_PARAMETER, IAM_UNAUTH_ROLE_PARAMETER } from '../utils/authType'; +import { TransformConfig } from '../config'; import * as SyncUtils from './sync-utils'; import Template, { DeploymentResources } from './types'; @@ -42,7 +43,7 @@ import { matchInputFieldDirective, sortTransformerPlugins, } from './utils'; -import { validateModelSchema } from './validation'; +import { validateModelSchema, validateAuthModes } from './validation'; // eslint-disable-next-line @typescript-eslint/ban-types function isFunction(obj: any): obj is Function { @@ -70,6 +71,7 @@ export interface GraphQLTransformOptions { readonly stacks?: Record; readonly featureFlags?: FeatureFlagProvider; readonly host?: TransformHostProvider; + readonly sandboxModeEnabled?: boolean; } export type StackMapping = { [resourceId: string]: string }; export class GraphQLTransform { @@ -103,6 +105,8 @@ export class GraphQLTransform { additionalAuthenticationProviders: [], }; + validateAuthModes(this.authConfig); + this.buildParameters = options.buildParameters || {}; this.stackMappingOverrides = options.stackMapping || {}; this.transformConfig = options.transformConfig || {}; @@ -124,6 +128,7 @@ export class GraphQLTransform { this.app, parsedDocument, this.stackMappingOverrides, + this.authConfig, this.options.featureFlags, this.transformConfig.ResolverConfig, ); @@ -135,7 +140,9 @@ export class GraphQLTransform { aws_api_key: true, aws_iam: true, aws_oidc: true, + aws_lambda: true, aws_cognito_user_pools: true, + allow_public_data_access_with_api_key: true, deprecated: true, }, ); @@ -143,6 +150,7 @@ export class GraphQLTransform { for (const transformer of this.transformers) { allModelDefinitions = allModelDefinitions.concat(...transformer.typeDefinitions, transformer.directive); } + const errors = validateModelSchema({ kind: Kind.DOCUMENT, definitions: allModelDefinitions, @@ -214,7 +222,6 @@ export class GraphQLTransform { // Synth the API and make it available to allow transformer plugins to manipulate the API const stackManager = context.stackManager as StackManager; const output: TransformerOutput = context.output as TransformerOutput; - const api = this.generateGraphQlApi(stackManager, output); // generate resolvers @@ -258,6 +265,8 @@ export class GraphQLTransform { name: `${apiName}-${envName.valueAsString}`, authorizationConfig, host: this.options.host, + sandboxModeEnabled: this.options.sandboxModeEnabled, + environmentName: envName.valueAsString, }); const authModes = [authorizationConfig.defaultAuthorization, ...(authorizationConfig.additionalAuthorizationModes || [])].map( mode => mode?.authorizationType, @@ -286,6 +295,11 @@ export class GraphQLTransform { }); } + if (authModes.includes(AuthorizationType.IAM)) { + stackManager.addParameter(IAM_AUTH_ROLE_PARAMETER, { type: 'String' }); + stackManager.addParameter(IAM_UNAUTH_ROLE_PARAMETER, { type: 'String' }); + } + new CfnOutput(rootStack, 'GraphQLAPIIdOutput', { value: api.apiId, description: 'Your GraphQL API ID.', diff --git a/packages/amplify-graphql-transformer-core/src/transformation/transformer-config.ts b/packages/amplify-graphql-transformer-core/src/transformation/transformer-config.ts new file mode 100644 index 00000000000..acbc9462fad --- /dev/null +++ b/packages/amplify-graphql-transformer-core/src/transformation/transformer-config.ts @@ -0,0 +1,115 @@ +export interface TransformMigrationConfig { + V1?: { + Resources: string[]; + }; +} + +// Auth Config +export type AppSyncAuthMode = 'API_KEY' | 'AMAZON_COGNITO_USER_POOLS' | 'AWS_IAM' | 'OPENID_CONNECT' | 'AWS_LAMBDA'; +export type AppSyncAuthConfiguration = { + defaultAuthentication: AppSyncAuthConfigurationEntry; + additionalAuthenticationProviders: Array; +}; + +export type AppSyncAuthConfigurationEntry = AppSyncAuthConfigurationUserPoolEntry | AppSyncAuthConfigurationAPIKeyEntry | AppSyncAuthConfigurationIAMEntry | AppSyncAuthConfigurationOIDCEntry | AppSyncAuthConfigurationLambdaEntry; +export type AppSyncAuthConfigurationAPIKeyEntry = { + authenticationType: 'API_KEY'; + apiKeyConfig: ApiKeyConfig; +}; +export type AppSyncAuthConfigurationUserPoolEntry = { + authenticationType: 'AMAZON_COGNITO_USER_POOLS'; + userPoolConfig: UserPoolConfig; +}; +export type AppSyncAuthConfigurationIAMEntry = { + authenticationType: 'AWS_IAM'; +}; + +export type AppSyncAuthConfigurationOIDCEntry = { + authenticationType: 'OPENID_CONNECT'; + openIDConnectConfig: OpenIDConnectConfig; +}; + +export type AppSyncAuthConfigurationLambdaEntry = { + authenticationType: 'AWS_LAMBDA'; + lambdaAuthorizerConfig: LambdaAuthorizerConfig; +}; + +export type ApiKeyConfig = { + description?: string; + apiKeyExpirationDays: number; +}; +export type UserPoolConfig = { + userPoolId: string; +}; +export type OpenIDConnectConfig = { + name: string; + issuerUrl: string; + clientId?: string; + iatTTL?: number; + authTTL?: number; +}; +export type LambdaAuthorizerConfig = { + lambdaFunction: string; + ttlSeconds?: number; +}; + +// Sync Config +export const enum ConflictHandlerType { + OPTIMISTIC = 'OPTIMISTIC_CONCURRENCY', + AUTOMERGE = 'AUTOMERGE', + LAMBDA = 'LAMBDA', +} + +export type ConflictDetectionType = 'VERSION' | 'NONE'; +export type SyncConfigOPTIMISTIC = { + ConflictDetection: ConflictDetectionType; + ConflictHandler: ConflictHandlerType.OPTIMISTIC; +}; +export type SyncConfigSERVER = { + ConflictDetection: ConflictDetectionType; + ConflictHandler: ConflictHandlerType.AUTOMERGE; +}; +export type SyncConfigLAMBDA = { + ConflictDetection: ConflictDetectionType; + ConflictHandler: ConflictHandlerType.LAMBDA; + LambdaConflictHandler: { + name: string; + region?: string; + lambdaArn?: any; + }; +}; +export type SyncConfig = SyncConfigOPTIMISTIC | SyncConfigSERVER | SyncConfigLAMBDA; + +export type ResolverConfig = { + project?: SyncConfig; + models?: { + [key: string]: SyncConfig; + }; +}; +export interface TransformConfig { + /** + * The transform library uses a "StackMapping" to determine which stack + * a particular resource belongs to. This "StackMapping" allows individual + * transformer implementations to add resources to a single context and + * reference resources as if they were all members of the same stack. The + * transform formatter takes the single context and the stack mapping + * and splits the context into a valid nested stack where any Fn::Ref or Fn::GetAtt + * is replaced by a Import/Export or Parameter. Users may provide mapping + * overrides to get specific behavior out of the transformer. Users may + * override the default stack mapping to customize behavior. + */ + StackMapping?: { + [resourceId: string]: string; + }; + + /** + * Provide build time options to GraphQL Transformer constructor functions. + * Certain options cannot be configured via CloudFormation parameters and + * need to be set at build time. E.G. DeletionPolicies cannot depend on parameters. + */ + TransformerOptions?: { + [transformer: string]: { + [option: string]: any; + }; + }; +} diff --git a/packages/amplify-graphql-transformer-core/src/transformation/transformer-plugin-base.ts b/packages/amplify-graphql-transformer-core/src/transformation/transformer-plugin-base.ts index f0f9c86d910..9dad23a4873 100644 --- a/packages/amplify-graphql-transformer-core/src/transformation/transformer-plugin-base.ts +++ b/packages/amplify-graphql-transformer-core/src/transformation/transformer-plugin-base.ts @@ -10,6 +10,7 @@ import { DataSourceInstance, TransformerPluginProvider, TransformerModelEnhancementProvider, + TransformerAuthProvider, } from '@aws-amplify/graphql-transformer-interfaces'; import { @@ -176,3 +177,9 @@ export abstract class TransformerModelEnhancerBase extends TransformerModelBase super(name, doc, type); } } + +export abstract class TransformerAuthBase extends TransformerPluginBase implements TransformerAuthProvider { + constructor(name: string, doc: DocumentNode | string, type: TransformerPluginType = TransformerPluginType.AUTH) { + super(name, doc, type); + } +} diff --git a/packages/amplify-graphql-transformer-core/src/transformation/utils.ts b/packages/amplify-graphql-transformer-core/src/transformation/utils.ts index e7cb2863963..24523f2256c 100644 --- a/packages/amplify-graphql-transformer-core/src/transformation/utils.ts +++ b/packages/amplify-graphql-transformer-core/src/transformation/utils.ts @@ -171,6 +171,7 @@ export function sortTransformerPlugins(plugins: TransformerPluginProvider[]): Tr TransformerPluginType.DATA_SOURCE_PROVIDER, TransformerPluginType.DATA_SOURCE_ENHANCER, TransformerPluginType.GENERIC, + TransformerPluginType.AUTH, ]; return plugins.sort((a, b) => { const aIdx = SORT_ORDER.indexOf(a.pluginType); diff --git a/packages/amplify-graphql-transformer-core/src/transformation/validation.ts b/packages/amplify-graphql-transformer-core/src/transformation/validation.ts index 4e94fd7b6e4..8d6bbd68434 100644 --- a/packages/amplify-graphql-transformer-core/src/transformation/validation.ts +++ b/packages/amplify-graphql-transformer-core/src/transformation/validation.ts @@ -54,6 +54,9 @@ import { NoUndefinedVariables } from 'graphql/validation/rules/NoUndefinedVariab import { NoUnusedVariables } from 'graphql/validation/rules/NoUnusedVariables'; import { UniqueDirectivesPerLocation } from 'graphql/validation/rules/UniqueDirectivesPerLocation'; +// AuthMode Types +import { AppSyncAuthConfiguration, AppSyncAuthMode } from '@aws-amplify/graphql-transformer-interfaces'; + /** * This set includes all validation rules defined by the GraphQL spec. * @@ -109,6 +112,7 @@ directive @aws_api_key on FIELD_DEFINITION | OBJECT directive @aws_iam on FIELD_DEFINITION | OBJECT directive @aws_oidc on FIELD_DEFINITION | OBJECT directive @aws_cognito_user_pools(cognito_groups: [String!]) on FIELD_DEFINITION | OBJECT +directive @allow_public_data_access_with_api_key(in: [String!]) on OBJECT # Allows transformer libraries to deprecate directive arguments. directive @deprecated(reason: String) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ENUM | ENUM_VALUE @@ -142,3 +146,21 @@ export const validateModelSchema = (doc: DocumentNode) => { const schema = buildASTSchema(fullDocument); return validate(schema, fullDocument, specifiedRules); }; + +export const validateAuthModes = (authConfig: AppSyncAuthConfiguration) => { + let additionalAuthModes: AppSyncAuthMode[] = []; + + if (authConfig.additionalAuthenticationProviders) { + additionalAuthModes = authConfig.additionalAuthenticationProviders.map(p => p.authenticationType).filter(t => !!t); + } + + const authModes: AppSyncAuthMode[] = [...additionalAuthModes, authConfig.defaultAuthentication.authenticationType]; + + for (let i = 0; i < authModes.length; i++) { + const mode = authModes[i]; + + if (mode !== 'API_KEY' && mode !== 'AMAZON_COGNITO_USER_POOLS' && mode !== 'AWS_IAM' && mode !== 'OPENID_CONNECT' && mode !== 'AWS_LAMBDA') { + throw new Error(`Invalid auth mode ${mode}`); + } + } +}; diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts index ded8e6cd888..544438499a3 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts @@ -23,4 +23,8 @@ export class TransformerDataSourceManager implements TransformerDataSourceManage collectDataSources = (): Readonly> => { return this.dataSourceMap; }; + + has = (name: string): boolean => { + return this.dataSourceMap.has(name); + }; } diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/index.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/index.ts index 59f32bab104..266dc3a4f79 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/index.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/index.ts @@ -5,7 +5,9 @@ import { TransformerContextOutputProvider, TransformerContextProvider, TransformerDataSourceManagerProvider, + AppSyncAuthConfiguration, } from '@aws-amplify/graphql-transformer-interfaces'; +import { TransformerContextMetadataProvider } from '@aws-amplify/graphql-transformer-interfaces/src/transformer-context/transformer-context-provider'; import { App } from '@aws-cdk/core'; import { DocumentNode } from 'graphql'; import { ResolverConfig } from '../config/transformer-config'; @@ -17,6 +19,25 @@ import { TransformerContextProviderRegistry } from './provider-registry'; import { ResolverManager } from './resolver'; import { TransformerResourceHelper } from './resource-helper'; import { StackManager } from './stack-manager'; +export { TransformerResolver } from './resolver'; +export class TransformerContextMetadata implements TransformerContextMetadataProvider { + /** + * Used by transformers to pass information between one another. + */ + private metadata: { [key: string]: any } = new Map(); + + public get(key: string): T | undefined { + return this.metadata[key] as T; + } + + public set(key: string, val: T): void { + this.metadata[key] = val; + } + + public has(key: string) { + return this.metadata[key] !== undefined; + } +} export class TransformerContext implements TransformerContextProvider { public readonly output: TransformerContextOutputProvider; @@ -27,12 +48,15 @@ export class TransformerContext implements TransformerContextProvider { public readonly resourceHelper: TransformerResourceHelper; public readonly featureFlags: FeatureFlagProvider; public _api?: GraphQLAPIProvider; + public readonly authConfig: AppSyncAuthConfiguration; private resolverConfig: ResolverConfig | undefined; + public metadata: TransformerContextMetadata; constructor( app: App, public readonly inputDocument: DocumentNode, stackMapping: Record, + authConfig: AppSyncAuthConfiguration, featureFlags?: FeatureFlagProvider, resolverConfig?: ResolverConfig, ) { @@ -42,9 +66,11 @@ export class TransformerContext implements TransformerContextProvider { this.providerRegistry = new TransformerContextProviderRegistry(); const stackManager = new StackManager(app, stackMapping); this.stackManager = stackManager; + this.authConfig = authConfig; this.resourceHelper = new TransformerResourceHelper(stackManager); this.featureFlags = featureFlags ?? new NoopFeatureFlagProvider(); this.resolverConfig = resolverConfig; + this.metadata = new TransformerContextMetadata(); } /** diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/output.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/output.ts index 51491093881..d8429a657af 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/output.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/output.ts @@ -586,7 +586,7 @@ export class TransformerOutput implements TransformerContextOutputProvider { kind: 'Document', definitions: Object.values(this.nodeMap), }, - ['aws_subscribe', 'aws_auth', 'aws_api_key', 'aws_iam', 'aws_oidc', 'aws_cognito_user_pools', 'deprecated'], + ['aws_subscribe', 'aws_auth', 'aws_api_key', 'aws_iam', 'aws_oidc', 'aws_cognito_user_pools', 'aws_lambda', 'deprecated'], ); const SDL = print(astSansDirectives); return SDL; diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/resolver.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/resolver.ts index ec0dc0059ba..00e8128bd0a 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/resolver.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/resolver.ts @@ -7,13 +7,14 @@ import { TransformerResolverProvider, TransformerResolversManagerProvider, } from '@aws-amplify/graphql-transformer-interfaces'; -import { CfnFunctionConfiguration } from '@aws-cdk/aws-appsync'; -import { isResolvableObject, Stack } from '@aws-cdk/core'; +import { AuthorizationType, CfnFunctionConfiguration } from '@aws-cdk/aws-appsync'; +import { isResolvableObject, Stack, CfnParameter } from '@aws-cdk/core'; import assert from 'assert'; import { toPascalCase } from 'graphql-transformer-common'; import { dedent } from 'ts-dedent'; import { MappingTemplate, S3MappingTemplate } from '../cdk-compat'; import * as SyncUtils from '../transformation/sync-utils'; +import { IAM_AUTH_ROLE_PARAMETER, IAM_UNAUTH_ROLE_PARAMETER } from '../utils'; import { StackManager } from './stack-manager'; type Slot = { @@ -95,6 +96,11 @@ export class ResolverManager implements TransformerResolversManagerProvider { } }; + hasResolver = (typeName: string, fieldName: string): boolean => { + const key = `${typeName}.${fieldName}`; + return this.resolvers.has(key); + }; + removeResolver = (typeName: string, fieldName: string): TransformerResolverProvider => { const key = `${typeName}.${fieldName}`; if (this.resolvers.has(key)) { @@ -234,24 +240,38 @@ export class TransformerResolver implements TransformerResolverProvider { } break; default: - throw new Error('Unknow DataSource type'); + throw new Error('Unknown DataSource type'); } } + let initResolver = dedent` + $util.qr($ctx.stash.put("typeName", "${this.typeName}")) + $util.qr($ctx.stash.put("fieldName", "${this.fieldName}")) + $util.qr($ctx.stash.put("conditions", [])) + $util.qr($ctx.stash.put("metadata", {})) + $util.qr($ctx.stash.metadata.put("dataSourceType", "${dataSourceType}")) + $util.qr($ctx.stash.metadata.put("apiId", "${api.apiId}")) + ${dataSource} + `; + const authModes = [context.authConfig.defaultAuthentication, ...(context.authConfig.additionalAuthenticationProviders || [])].map( + mode => mode?.authenticationType, + ); + if (authModes.includes(AuthorizationType.IAM)) { + const authRoleParameter = (context.stackManager.getParameter(IAM_AUTH_ROLE_PARAMETER) as CfnParameter).valueAsString; + const unauthRoleParameter = (context.stackManager.getParameter(IAM_UNAUTH_ROLE_PARAMETER) as CfnParameter).valueAsString; + initResolver += dedent`\n + $util.qr($ctx.stash.put("authRole", "arn:aws:sts::${ + Stack.of(context.stackManager.rootStack).account + }:assumed-role/${authRoleParameter}/CognitoIdentityCredentials")) + $util.qr($ctx.stash.put("unauthRole", "arn:aws:sts::${ + Stack.of(context.stackManager.rootStack).account + }:assumed-role/${unauthRoleParameter}/CognitoIdentityCredentials")) + `; + } + initResolver += '\n$util.toJson({})'; api.host.addResolver( this.typeName, this.fieldName, - MappingTemplate.inlineTemplateFromString( - dedent` - $util.qr($ctx.stash.put("typeName", "${this.typeName}")) - $util.qr($ctx.stash.put("fieldName", "${this.fieldName}")) - $util.qr($ctx.stash.put("conditions", [])) - $util.qr($ctx.stash.put("metadata", {})) - $util.qr($ctx.stash.metadata.put("dataSourceType", "${dataSourceType}")) - $util.qr($ctx.stash.metadata.put("apiId", "${api.apiId}")) - ${dataSource} - $util.toJson({}) - `, - ), + MappingTemplate.inlineTemplateFromString(initResolver), MappingTemplate.inlineTemplateFromString('$util.toJson($ctx.prev.result)'), undefined, [...requestFns, dataSourceProviderFn, ...responseFns].map(fn => fn.functionId), diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/stack-manager.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/stack-manager.ts index 003f41ec957..40faf98fe1c 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/stack-manager.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/stack-manager.ts @@ -23,6 +23,7 @@ export class StackManager implements StackManagerProvider { } createStack = (stackName: string): Stack => { const newStack = new TransformerNestedStack(this.rootStack, stackName); + this.stacks.set(stackName, newStack); return newStack; }; diff --git a/packages/amplify-graphql-transformer-core/src/utils/authType.ts b/packages/amplify-graphql-transformer-core/src/utils/authType.ts index 2be8d32a082..881b6443546 100644 --- a/packages/amplify-graphql-transformer-core/src/utils/authType.ts +++ b/packages/amplify-graphql-transformer-core/src/utils/authType.ts @@ -1,15 +1,20 @@ import { AuthorizationConfig, AuthorizationMode, AuthorizationType } from '@aws-cdk/aws-appsync'; import { UserPool } from '@aws-cdk/aws-cognito'; import { Duration, Expiration } from '@aws-cdk/core'; -import { AppSyncAuthConfiguration, AppSyncAuthConfigurationEntry, AppSyncAuthMode } from '../config'; import { StackManager } from '../transformer-context/stack-manager'; +import { AppSyncAuthConfiguration, AppSyncAuthConfigurationEntry, AppSyncAuthMode } from '@aws-amplify/graphql-transformer-interfaces'; -const authTypeMap: Record = { +const authTypeMap: Record = { API_KEY: AuthorizationType.API_KEY, AMAZON_COGNITO_USER_POOLS: AuthorizationType.USER_POOL, AWS_IAM: AuthorizationType.IAM, OPENID_CONNECT: AuthorizationType.OIDC, + AWS_LAMBDA: "AWS_LAMBDA", }; + +export const IAM_AUTH_ROLE_PARAMETER = 'authRoleName'; +export const IAM_UNAUTH_ROLE_PARAMETER = 'unauthRoleName'; + export function adoptAuthModes(stack: StackManager, authConfig: AppSyncAuthConfiguration): AuthorizationConfig { return { defaultAuthorization: adoptAuthMode(stack, authConfig.defaultAuthentication), @@ -17,15 +22,17 @@ export function adoptAuthModes(stack: StackManager, authConfig: AppSyncAuthConfi }; } -export function adoptAuthMode(stackManager: StackManager, entry: AppSyncAuthConfigurationEntry): AuthorizationMode { +export function adoptAuthMode(stackManager: StackManager, entry: AppSyncAuthConfigurationEntry): AuthorizationMode | any { const authType = authTypeMap[entry.authenticationType]; switch (entry.authenticationType) { case AuthorizationType.API_KEY: return { authorizationType: authType, apiKeyConfig: { - description: entry.apiKeyConfig.description, - expires: Expiration.after(Duration.days(entry.apiKeyConfig.apiKeyExpirationDays)), + description: entry.apiKeyConfig?.description, + expires: entry.apiKeyConfig?.apiKeyExpirationDays + ? Expiration.after(Duration.days(entry.apiKeyConfig.apiKeyExpirationDays)) + : undefined, }, }; case AuthorizationType.USER_POOL: @@ -53,6 +60,14 @@ export function adoptAuthMode(stackManager: StackManager, entry: AppSyncAuthConf tokenExpiryFromIssue: strToNumber(entry.openIDConnectConfig!.iatTTL), }, }; + case 'AWS_LAMBDA': + return { + authorizationType: authType, + lambdaAuthorizerConfig: { + lambdaFunction: entry.lambdaAuthorizerConfig!.lambdaFunction, + ttlSeconds: strToNumber(entry.lambdaAuthorizerConfig!.ttlSeconds), + }, + }; default: throw new Error('Invalid auth config'); } diff --git a/packages/amplify-graphql-transformer-core/src/utils/index.ts b/packages/amplify-graphql-transformer-core/src/utils/index.ts index ab2e7da49c9..7389a33dfb1 100644 --- a/packages/amplify-graphql-transformer-core/src/utils/index.ts +++ b/packages/amplify-graphql-transformer-core/src/utils/index.ts @@ -2,3 +2,4 @@ export { DirectiveWrapper } from './directive-wrapper'; export { collectDirectives, collectDirectivesByTypeNames } from './type-map-utils'; export { stripDirectives } from './strip-directives'; export { DEFAULT_SCHEMA_DEFINITION } from './defaultSchema'; +export { IAM_AUTH_ROLE_PARAMETER, IAM_UNAUTH_ROLE_PARAMETER } from './authType'; diff --git a/packages/amplify-graphql-transformer-interfaces/CHANGELOG.md b/packages/amplify-graphql-transformer-interfaces/CHANGELOG.md index 60f87d2f8c5..08c37432208 100644 --- a/packages/amplify-graphql-transformer-interfaces/CHANGELOG.md +++ b/packages/amplify-graphql-transformer-interfaces/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.10.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-transformer-interfaces@1.9.0...@aws-amplify/graphql-transformer-interfaces@1.10.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **graphql-model-transformer:** [@model](https://github.com/model) conflict resolution ([#8035](https://github.com/aws-amplify/amplify-cli/issues/8035)) ([f3bdc4a](https://github.com/aws-amplify/amplify-cli/commit/f3bdc4ac1fcf596f634d9d2e968785e76f7b138c)) +* **graphql-model-transformer:** iam role name does not exceed 64 characters ([#8244](https://github.com/aws-amplify/amplify-cli/issues/8244)) ([812a671](https://github.com/aws-amplify/amplify-cli/commit/812a67163d6dd33160bf7ace9afd538c83a7af1a)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + # [1.9.0](https://github.com/aws-amplify/amplify-cli/compare/@aws-amplify/graphql-transformer-interfaces@1.8.2...@aws-amplify/graphql-transformer-interfaces@1.9.0) (2021-09-02) diff --git a/packages/amplify-graphql-transformer-interfaces/package.json b/packages/amplify-graphql-transformer-interfaces/package.json index f382142ff90..ad609a4d713 100644 --- a/packages/amplify-graphql-transformer-interfaces/package.json +++ b/packages/amplify-graphql-transformer-interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/graphql-transformer-interfaces", - "version": "1.9.0", + "version": "1.10.0-graphql-vnext-dev-preview.0", "description": "Amplify GraphQL transformer interface definitions", "repository": { "type": "git", diff --git a/packages/amplify-graphql-transformer-interfaces/src/graphql-api-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/graphql-api-provider.ts index 4e7ce993724..69cd8f78c71 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/graphql-api-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/graphql-api-provider.ts @@ -1,6 +1,63 @@ import { CfnResource, Construct, IAsset, IConstruct } from '@aws-cdk/core'; import { Grant, IGrantable, IRole } from '@aws-cdk/aws-iam'; -import {TransformHostProvider} from './transform-host-provider'; +import { TransformHostProvider } from './transform-host-provider'; + +// Auth Config +export type AppSyncAuthMode = 'API_KEY' | 'AMAZON_COGNITO_USER_POOLS' | 'AWS_IAM' | 'OPENID_CONNECT' | 'AWS_LAMBDA'; +export type AppSyncAuthConfiguration = { + defaultAuthentication: AppSyncAuthConfigurationEntry; + additionalAuthenticationProviders: Array; +}; + +export type AppSyncAuthConfigurationEntry = + | AppSyncAuthConfigurationUserPoolEntry + | AppSyncAuthConfigurationAPIKeyEntry + | AppSyncAuthConfigurationIAMEntry + | AppSyncAuthConfigurationOIDCEntry + | AppSyncAuthConfigurationLambdaEntry; + +export type AppSyncAuthConfigurationAPIKeyEntry = { + authenticationType: 'API_KEY'; + apiKeyConfig?: ApiKeyConfig; +}; +export type AppSyncAuthConfigurationUserPoolEntry = { + authenticationType: 'AMAZON_COGNITO_USER_POOLS'; + userPoolConfig?: UserPoolConfig; +}; +export type AppSyncAuthConfigurationIAMEntry = { + authenticationType: 'AWS_IAM'; +}; + +export type AppSyncAuthConfigurationOIDCEntry = { + authenticationType: 'OPENID_CONNECT'; + openIDConnectConfig?: OpenIDConnectConfig; +}; + +export type AppSyncAuthConfigurationLambdaEntry = { + authenticationType: 'AWS_LAMBDA'; + lambdaAuthorizerConfig?: LambdaConfig; +}; + +export interface ApiKeyConfig { + description?: string; + apiKeyExpirationDays: number; + apiKeyExpirationDate?: Date; +} +export interface UserPoolConfig { + userPoolId: string; +} +export interface OpenIDConnectConfig { + name: string; + issuerUrl: string; + clientId?: string; + iatTTL?: number; + authTTL?: number; +} + +export interface LambdaConfig { + lambdaFunction: string; + ttlSeconds?: number; +} export interface AppSyncFunctionConfigurationProvider extends IConstruct { readonly arn: string; diff --git a/packages/amplify-graphql-transformer-interfaces/src/index.ts b/packages/amplify-graphql-transformer-interfaces/src/index.ts index 5271dfc2c34..36be2b3a84a 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/index.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/index.ts @@ -21,8 +21,9 @@ export { MutationFieldType, QueryFieldType, SubscriptionFieldType, - TransformerModelEnhancementProvider, TransformerModelProvider, + TransformerModelEnhancementProvider, + TransformerAuthProvider, } from './transformer-model-provider'; export { FeatureFlagProvider } from './feature-flag-provider'; @@ -36,6 +37,15 @@ export { InlineMappingTemplateProvider, APIIAMResourceProvider, TemplateType as MappingTemplateType, + AppSyncAuthConfiguration, + AppSyncAuthConfigurationAPIKeyEntry, + AppSyncAuthConfigurationEntry, + AppSyncAuthConfigurationIAMEntry, + ApiKeyConfig, + AppSyncAuthConfigurationOIDCEntry, + AppSyncAuthConfigurationUserPoolEntry, + AppSyncAuthMode, + UserPoolConfig, } from './graphql-api-provider'; export { TransformHostProvider, DynamoDbDataSourceOptions } from './transform-host-provider'; diff --git a/packages/amplify-graphql-transformer-interfaces/src/transform-host-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transform-host-provider.ts index 6ef36c63475..de89ebcea1b 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transform-host-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transform-host-provider.ts @@ -73,4 +73,7 @@ export interface TransformHostProvider { getDataSource: (name: string) => BaseDataSource | void; hasDataSource: (name: string) => boolean; + + getResolver: (typeName: string, fieldName: string) => CfnResolver | void; + hasResolver: (typeName: string, fieldName: string) => boolean; } diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/stack-manager-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/stack-manager-provider.ts index b528c7eeec1..70d1e23842b 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/stack-manager-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/stack-manager-provider.ts @@ -1,6 +1,7 @@ import { CfnParameter, CfnParameterProps, Stack } from '@aws-cdk/core'; export interface StackManagerProvider { + readonly rootStack: Stack; getStack: (stackName: string) => Stack; createStack: (stackName: string) => Stack; hasStack: (stackName: string) => boolean; diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-context-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-context-provider.ts index 4102120b8a7..1d3c41c133e 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-context-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-context-provider.ts @@ -4,11 +4,18 @@ import { TransformerProviderRegistry } from './transformer-provider-registry'; import { DocumentNode } from 'graphql'; import { TransformerContextOutputProvider } from './transformer-context-output-provider'; import { StackManagerProvider } from './stack-manager-provider'; -import { GraphQLAPIProvider } from '../graphql-api-provider'; +import { AppSyncAuthConfiguration, GraphQLAPIProvider } from '../graphql-api-provider'; import { TransformerResourceProvider } from './resource-resource-provider'; import { FeatureFlagProvider } from '../feature-flag-provider'; +export interface TransformerContextMetadataProvider { + set(key: string, value: T): void; + get(key: string): T | undefined; + has(key: string): boolean; +} + export interface TransformerContextProvider { + metadata: TransformerContextMetadataProvider; resolvers: TransformerResolversManagerProvider; dataSources: TransformerDataSourceManagerProvider; providerRegistry: TransformerProviderRegistry; @@ -19,19 +26,38 @@ export interface TransformerContextProvider { api: GraphQLAPIProvider; resourceHelper: TransformerResourceProvider; featureFlags: FeatureFlagProvider; + authConfig: AppSyncAuthConfiguration; isProjectUsingDataStore(): boolean; getResolverConfig(): ResolverConfig | undefined; } -export type TransformerBeforeStepContextProvider = Pick; +export type TransformerBeforeStepContextProvider = Pick< + TransformerContextProvider, + 'inputDocument' | 'featureFlags' | 'isProjectUsingDataStore' | 'getResolverConfig' | 'authConfig' +>; export type TransformerSchemaVisitStepContextProvider = Pick< TransformerContextProvider, - 'inputDocument' | 'output' | 'providerRegistry' | 'featureFlags' | 'isProjectUsingDataStore' | 'getResolverConfig' + | 'inputDocument' + | 'output' + | 'providerRegistry' + | 'featureFlags' + | 'isProjectUsingDataStore' + | 'getResolverConfig' + | 'metadata' + | 'authConfig' >; export type TransformerValidationStepContextProvider = Pick< TransformerContextProvider, - 'inputDocument' | 'output' | 'providerRegistry' | 'dataSources' | 'featureFlags' | 'isProjectUsingDataStore' | 'getResolverConfig' + | 'inputDocument' + | 'output' + | 'providerRegistry' + | 'dataSources' + | 'featureFlags' + | 'isProjectUsingDataStore' + | 'getResolverConfig' + | 'metadata' + | 'authConfig' >; export type TransformerPrepareStepContextProvider = TransformerValidationStepContextProvider; export type TransformerTransformSchemaStepContextProvider = TransformerValidationStepContextProvider; diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts index c1472951639..8350f8be702 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts @@ -22,6 +22,7 @@ export type DataSourceInstance = ITable | CfnDomain | HttpDataSource | IFunction export interface TransformerDataSourceManagerProvider { add(type: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, dataSourceInstance: DataSourceInstance): void; get(type: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode): DataSourceInstance; + has(name: string): boolean; } export interface DataSourceProvider extends BackedDataSource {} diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-resolver-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-resolver-provider.ts index 2688c9c192b..b369c6bb234 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-resolver-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-resolver-provider.ts @@ -17,6 +17,7 @@ export interface TransformerResolverProvider { export interface TransformerResolversManagerProvider { addResolver: (typeName: string, fieldName: string, resolver: TransformerResolverProvider) => TransformerResolverProvider; getResolver: (typeName: string, fieldName: string) => TransformerResolverProvider | void; + hasResolver: (typeName: string, fieldName: string) => boolean; removeResolver: (typeName: string, fieldName: string) => TransformerResolverProvider; collectResolvers: () => Map; diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-model-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-model-provider.ts index 9bf6090705e..ee5d4372e67 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-model-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-model-provider.ts @@ -129,4 +129,6 @@ export interface TransformerModelProvider extends TransformerPluginProvider { ) => ObjectTypeDefinitionNode; } +export interface TransformerAuthProvider extends TransformerPluginProvider {} + export interface TransformerModelEnhancementProvider extends Partial {} diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-plugin-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-plugin-provider.ts index e506f490beb..076831255b5 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-plugin-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-plugin-provider.ts @@ -25,6 +25,7 @@ export enum TransformerPluginType { DATA_SOURCE_PROVIDER = 'DATA_SOURCE_PROVIDER', DATA_SOURCE_ENHANCER = 'DATA_SOURCE_ENHANCER', GENERIC = 'GENERIC', + AUTH = 'AUTH', } export interface TransformerPluginProvider { pluginType: TransformerPluginType; diff --git a/packages/amplify-headless-interface/CHANGELOG.md b/packages/amplify-headless-interface/CHANGELOG.md index 3844ed890d9..f2a9a8fc1fa 100644 --- a/packages/amplify-headless-interface/CHANGELOG.md +++ b/packages/amplify-headless-interface/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-headless-interface@1.10.0...amplify-headless-interface@1.11.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + # [1.10.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-headless-interface@1.9.0...amplify-headless-interface@1.10.0) (2021-08-06) diff --git a/packages/amplify-headless-interface/package.json b/packages/amplify-headless-interface/package.json index ae73d38c8d0..48323918446 100644 --- a/packages/amplify-headless-interface/package.json +++ b/packages/amplify-headless-interface/package.json @@ -1,6 +1,6 @@ { "name": "amplify-headless-interface", - "version": "1.10.0", + "version": "1.11.0-graphql-vnext-dev-preview.0", "description": "interfaces for amplify headless mode payloads", "main": "lib/index.js", "scripts": { diff --git a/packages/amplify-headless-interface/src/interface/api/add.ts b/packages/amplify-headless-interface/src/interface/api/add.ts index 51f44124538..593bbee4c6d 100644 --- a/packages/amplify-headless-interface/src/interface/api/add.ts +++ b/packages/amplify-headless-interface/src/interface/api/add.ts @@ -122,7 +122,8 @@ export type AppSyncAuthType = | AppSyncAPIKeyAuthType | AppSyncAWSIAMAuthType | AppSyncCognitoUserPoolsAuthType - | AppSyncOpenIDConnectAuthType; + | AppSyncOpenIDConnectAuthType + | AppSyncLambdaAuthType; /** * Specifies that the AppSync API should be secured using an API key. @@ -130,6 +131,7 @@ export type AppSyncAuthType = export interface AppSyncAPIKeyAuthType { mode: 'API_KEY'; expirationTime?: number; + apiKeyExpirationDate?: Date; keyDescription?: string; } @@ -162,3 +164,12 @@ export interface AppSyncOpenIDConnectAuthType { openIDAuthTTL?: string; openIDIatTTL?: string; } + +/** + * Specifies that the AppSync API should be secured using Lambda. + */ + export interface AppSyncLambdaAuthType { + mode: 'AWS_LAMBDA'; + lambdaFunction: string; + ttlSeconds?: string; +} \ No newline at end of file diff --git a/packages/amplify-java-function-runtime-provider/CHANGELOG.md b/packages/amplify-java-function-runtime-provider/CHANGELOG.md index 7d7f31d2c0b..4f7186b46db 100644 --- a/packages/amplify-java-function-runtime-provider/CHANGELOG.md +++ b/packages/amplify-java-function-runtime-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.8.15-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-java-function-runtime-provider@1.8.14...amplify-java-function-runtime-provider@1.8.15-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-java-function-runtime-provider + + + + + ## [1.8.14](https://github.com/aws-amplify/amplify-cli/compare/amplify-java-function-runtime-provider@1.8.13...amplify-java-function-runtime-provider@1.8.14) (2021-09-18) **Note:** Version bump only for package amplify-java-function-runtime-provider diff --git a/packages/amplify-java-function-runtime-provider/package.json b/packages/amplify-java-function-runtime-provider/package.json index 486e2e47981..c13f890a31a 100644 --- a/packages/amplify-java-function-runtime-provider/package.json +++ b/packages/amplify-java-function-runtime-provider/package.json @@ -1,6 +1,6 @@ { "name": "amplify-java-function-runtime-provider", - "version": "1.8.14", + "version": "1.8.15-graphql-vnext-dev-preview.0", "description": "Provides functionality related to functions in JAVA on AWS", "repository": { "type": "git", @@ -21,7 +21,7 @@ "clean": "rimraf lib tsconfig.tsbuildinfo" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", "execa": "^5.1.1", "fs-extra": "^8.1.0", diff --git a/packages/amplify-migration-tests/CHANGELOG.md b/packages/amplify-migration-tests/CHANGELOG.md index 392018a6b6e..0554287ab95 100644 --- a/packages/amplify-migration-tests/CHANGELOG.md +++ b/packages/amplify-migration-tests/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.2.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-migration-tests@3.1.9...amplify-migration-tests@3.2.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* **cli-api:** improve add and update api ([02af6b5](https://github.com/aws-amplify/amplify-cli/commit/02af6b582367b584ad755c889fb04722af355368)) + + + + + ## [3.1.9](https://github.com/aws-amplify/amplify-cli/compare/amplify-migration-tests@3.1.8...amplify-migration-tests@3.1.9) (2021-09-18) **Note:** Version bump only for package amplify-migration-tests diff --git a/packages/amplify-migration-tests/package.json b/packages/amplify-migration-tests/package.json index dc9be803854..8d629c084f0 100644 --- a/packages/amplify-migration-tests/package.json +++ b/packages/amplify-migration-tests/package.json @@ -1,6 +1,6 @@ { "name": "amplify-migration-tests", - "version": "3.1.9", + "version": "3.2.0-graphql-vnext-dev-preview.0", "description": "", "repository": { "type": "git", @@ -26,12 +26,12 @@ "setup-profile": "ts-node ./src/configure_tests.ts" }, "dependencies": { - "amplify-e2e-core": "1.26.1", + "amplify-e2e-core": "1.27.0-graphql-vnext-dev-preview.0", "aws-sdk": "^2.963.0", "dotenv": "^8.2.0", "esm": "^3.2.25", "fs-extra": "^8.1.0", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "rimraf": "^3.0.0", "uuid": "^8.2.0" }, diff --git a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts index 54c3b31ad63..982209a51f1 100644 --- a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts @@ -1,5 +1,5 @@ import { - addApiWithSchema, + addApiWithoutSchema, addFeatureFlag, amplifyPush, amplifyPushForce, @@ -28,7 +28,8 @@ describe('amplify key force push', () => { const initialSchema = 'migrations_key/simple_key.graphql'; // init, add api and push with installed cli await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); // gql-compile and force push with codebase cli await apiGqlCompile(projRoot, true); @@ -40,7 +41,8 @@ describe('amplify key force push', () => { const initialSchema = 'migrations_key/initial_schema.graphql'; // init, add api and push with installed cli await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); // add feature flag addFeatureFlag(projRoot, 'graphqltransformer', 'secondaryKeyAsGSI', true); diff --git a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.searchable.migration.test.ts b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.searchable.migration.test.ts index b1c7194472d..0b0c0b5d5e9 100644 --- a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.searchable.migration.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.searchable.migration.test.ts @@ -1,5 +1,5 @@ import { - addApiWithSchema, + addApiWithoutSchema, amplifyPush, amplifyPushUpdate, createNewProjectDir, @@ -26,7 +26,8 @@ describe('amplify searchable migration', () => { const nextSchema = 'migrations_searchable/updated_searchable.graphql'; // init, add api and push with installed cli await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); // update and push with codebase cli updateApiSchema(projRoot, projectName, nextSchema); diff --git a/packages/amplify-migration-tests/src/__tests__/update_tests/api_migration_update.test.ts b/packages/amplify-migration-tests/src/__tests__/update_tests/api_migration_update.test.ts index abdbef5886f..a2593d79c22 100644 --- a/packages/amplify-migration-tests/src/__tests__/update_tests/api_migration_update.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/update_tests/api_migration_update.test.ts @@ -1,6 +1,6 @@ import { - addApiWithSchema, - addApiWithSchemaAndConflictDetection, + addApiWithoutSchema, + addApiWithBlankSchemaAndConflictDetection, amplifyPush, amplifyPushUpdate, createNewProjectDir, @@ -38,7 +38,8 @@ describe('api migration update test', () => { const nextSchema = 'next_key_blog.graphql'; // init the project and add api with installed cli await initJSProjectWithProfile(projRoot, { name: projectName }); - await addApiWithSchema(projRoot, initialSchema); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, projectName, initialSchema); await amplifyPush(projRoot); // update api and push with the CLI to be released (the codebase) updateApiSchema(projRoot, projectName, nextSchema); @@ -53,7 +54,8 @@ describe('api migration update test', () => { it('api update migration with multiauth', async () => { // init and add api with installed CLI await initJSProjectWithProfile(projRoot, { name: 'simplemodelmultiauth' }); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'simplemodelmultiauth', 'simple_model.graphql'); // update and push with codebase await updateApiWithMultiAuth(projRoot, { testingWithLatestCodebase: true }); await amplifyPush(projRoot, true); @@ -98,7 +100,8 @@ describe('api migration update test', () => { const name = `syncenabled`; // init and add api with locally installed cli await initJSProjectWithProfile(projRoot, { name }); - await addApiWithSchemaAndConflictDetection(projRoot, 'simple_model.graphql'); + await addApiWithBlankSchemaAndConflictDetection(projRoot); + await updateApiSchema(projRoot, name, 'simple_model.graphql'); let transformConfig = getTransformConfig(projRoot, name); expect(transformConfig).toBeDefined(); diff --git a/packages/amplify-migration-tests/src/__tests__/update_tests/function_migration_update.test.ts b/packages/amplify-migration-tests/src/__tests__/update_tests/function_migration_update.test.ts index 1755e98afef..8e0adf6f47f 100644 --- a/packages/amplify-migration-tests/src/__tests__/update_tests/function_migration_update.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/update_tests/function_migration_update.test.ts @@ -1,5 +1,5 @@ import { - addApiWithSchema, + addApiWithoutSchema, addFunction, addLayer, amplifyPush, @@ -15,6 +15,7 @@ import { LayerRuntime, loadFunctionTestFile, overrideFunctionSrcNode, + updateApiSchema, updateFunction, validateLayerMetadata, } from 'amplify-e2e-core'; @@ -34,7 +35,9 @@ describe('amplify function migration', () => { }); it('existing lambda updated with additional permissions should be able to scan ddb', async () => { - await initJSProjectWithProfile(projRoot, {}); + await initJSProjectWithProfile(projRoot, { + name: 'lambdapermissionscanddb', + }); const random = Math.floor(Math.random() * 10000); const fnName = `integtestfn${random}`; @@ -58,7 +61,8 @@ describe('amplify function migration', () => { expect(functionName).toBeDefined(); expect(region).toBeDefined(); - await addApiWithSchema(projRoot, 'simple_model.graphql'); + await addApiWithoutSchema(projRoot); + await updateApiSchema(projRoot, 'lambdapermissionscanddb', 'simple_model.graphql'); await updateFunction( projRoot, { diff --git a/packages/amplify-nodejs-function-runtime-provider/CHANGELOG.md b/packages/amplify-nodejs-function-runtime-provider/CHANGELOG.md index 1275276e6cf..6619f4ae49b 100644 --- a/packages/amplify-nodejs-function-runtime-provider/CHANGELOG.md +++ b/packages/amplify-nodejs-function-runtime-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.12-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-nodejs-function-runtime-provider@1.6.11...amplify-nodejs-function-runtime-provider@1.6.12-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-nodejs-function-runtime-provider + + + + + ## [1.6.11](https://github.com/aws-amplify/amplify-cli/compare/amplify-nodejs-function-runtime-provider@1.6.10...amplify-nodejs-function-runtime-provider@1.6.11) (2021-09-18) **Note:** Version bump only for package amplify-nodejs-function-runtime-provider diff --git a/packages/amplify-nodejs-function-runtime-provider/package.json b/packages/amplify-nodejs-function-runtime-provider/package.json index 4574278ba69..1b6ca4ecd8c 100644 --- a/packages/amplify-nodejs-function-runtime-provider/package.json +++ b/packages/amplify-nodejs-function-runtime-provider/package.json @@ -1,6 +1,6 @@ { "name": "amplify-nodejs-function-runtime-provider", - "version": "1.6.11", + "version": "1.6.12-graphql-vnext-dev-preview.0", "description": "Provides functionality related to functions in NodeJS on AWS", "repository": { "type": "git", @@ -23,7 +23,7 @@ "test": "jest" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", "archiver": "^5.3.0", "execa": "^5.1.1", diff --git a/packages/amplify-nodejs-function-template-provider/CHANGELOG.md b/packages/amplify-nodejs-function-template-provider/CHANGELOG.md index ea3ce43b77b..e4b5beba8ce 100644 --- a/packages/amplify-nodejs-function-template-provider/CHANGELOG.md +++ b/packages/amplify-nodejs-function-template-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-nodejs-function-template-provider@1.6.21...amplify-nodejs-function-template-provider@1.6.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-nodejs-function-template-provider + + + + + ## [1.6.21](https://github.com/aws-amplify/amplify-cli/compare/amplify-nodejs-function-template-provider@1.6.20...amplify-nodejs-function-template-provider@1.6.21) (2021-09-18) **Note:** Version bump only for package amplify-nodejs-function-template-provider diff --git a/packages/amplify-nodejs-function-template-provider/package.json b/packages/amplify-nodejs-function-template-provider/package.json index a51c208efdb..30c5fc8e098 100644 --- a/packages/amplify-nodejs-function-template-provider/package.json +++ b/packages/amplify-nodejs-function-template-provider/package.json @@ -1,6 +1,6 @@ { "name": "amplify-nodejs-function-template-provider", - "version": "1.6.21", + "version": "1.6.22-graphql-vnext-dev-preview.0", "description": "Node JS templates supplied by the Amplify Team", "repository": { "type": "git", @@ -21,9 +21,9 @@ "clean": "rimraf lib tsconfig.tsbuildinfo" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/amplify-prompts/CHANGELOG.md b/packages/amplify-prompts/CHANGELOG.md index 82f5d957849..a56fceebe6f 100644 --- a/packages/amplify-prompts/CHANGELOG.md +++ b/packages/amplify-prompts/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.2.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-prompts@1.1.2...amplify-prompts@1.2.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) +* update pick styling, hint text, and ctrl+z listener ([#8167](https://github.com/aws-amplify/amplify-cli/issues/8167)) ([620a36a](https://github.com/aws-amplify/amplify-cli/commit/620a36a8d4596cfd686820a5556431077977db5a)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [1.1.2](https://github.com/aws-amplify/amplify-cli/compare/amplify-prompts@1.1.1...amplify-prompts@1.1.2) (2021-09-02) **Note:** Version bump only for package amplify-prompts diff --git a/packages/amplify-prompts/package.json b/packages/amplify-prompts/package.json index 54584e0ef4a..26af1ad4ba6 100644 --- a/packages/amplify-prompts/package.json +++ b/packages/amplify-prompts/package.json @@ -1,6 +1,6 @@ { "name": "amplify-prompts", - "version": "1.1.2", + "version": "1.2.0-graphql-vnext-dev-preview.0", "description": "Utility functions for Amplify CLI terminal I/O", "main": "lib/index.js", "scripts": { diff --git a/packages/amplify-prompts/src/validators.ts b/packages/amplify-prompts/src/validators.ts index 9651f4125dc..463f6e958ed 100644 --- a/packages/amplify-prompts/src/validators.ts +++ b/packages/amplify-prompts/src/validators.ts @@ -32,11 +32,6 @@ export const minLength = (input: string) => input.length < minLen ? message || `Input must be more than ${minLen} characters long` : true; -export const exact = - (expected: string, message?: string): Validator => - (input: string) => - input === expected ? true : message ?? 'Input does not match expected value'; - /** * Logically "and"s several validators * If a validator returns an error message, that message is returned by this function, unless an override message is specified diff --git a/packages/amplify-provider-awscloudformation/CHANGELOG.md b/packages/amplify-provider-awscloudformation/CHANGELOG.md index e22617bdeda..efd8df60d55 100644 --- a/packages/amplify-provider-awscloudformation/CHANGELOG.md +++ b/packages/amplify-provider-awscloudformation/CHANGELOG.md @@ -3,6 +3,35 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.61.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-provider-awscloudformation@4.60.1...amplify-provider-awscloudformation@4.61.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* add pull check for missing s3 objects, ref [#8233](https://github.com/aws-amplify/amplify-cli/issues/8233) ([#8250](https://github.com/aws-amplify/amplify-cli/issues/8250)) ([d17309d](https://github.com/aws-amplify/amplify-cli/commit/d17309d36679f7b0c2fee858f38c44618e885370)) +* **amplify-cli-core:** add service mapping FFs ([#7024](https://github.com/aws-amplify/amplify-cli/issues/7024)) ([36fe24d](https://github.com/aws-amplify/amplify-cli/commit/36fe24db9f37a8a12d50f1e20ea44562eb44d04a)) +* **amplify-provider-awscloudformation:** invoke api function from invoker plugin ([#8274](https://github.com/aws-amplify/amplify-cli/issues/8274)) ([d30f1d6](https://github.com/aws-amplify/amplify-cli/commit/d30f1d65e3b2cf259cb0382389b6ae27d914d9cc)) +* **amplify-provider-awscloudformation:** remove sandbox mode directive from schema before transform ([#8272](https://github.com/aws-amplify/amplify-cli/issues/8272)) ([17aa0a2](https://github.com/aws-amplify/amplify-cli/commit/17aa0a2fd9356e05ff30e76b125554dbe7564e80)) +* **graphql-model-transformer:** [@model](https://github.com/model) conflict resolution ([#8035](https://github.com/aws-amplify/amplify-cli/issues/8035)) ([f3bdc4a](https://github.com/aws-amplify/amplify-cli/commit/f3bdc4ac1fcf596f634d9d2e968785e76f7b138c)) +* **graphql-model-transformer:** provide correct directive definitions based on transformer version ([#8208](https://github.com/aws-amplify/amplify-cli/issues/8208)) ([5583cd4](https://github.com/aws-amplify/amplify-cli/commit/5583cd47e620992ea9df1f02d812577dc90391eb)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* add [@many](https://github.com/many)ToMany directive ([#8195](https://github.com/aws-amplify/amplify-cli/issues/8195)) ([cc644eb](https://github.com/aws-amplify/amplify-cli/commit/cc644ebc4968f29ad6b3f0b42013d7ee6a142f7e)) +* **amplify-provider-awscloudformation:** match env directive field for sandbox mode ([#3](https://github.com/aws-amplify/amplify-cli/issues/3)) ([3158549](https://github.com/aws-amplify/amplify-cli/commit/31585492fd4c358903d68d9640e9ac53e9b32eef)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [4.60.1](https://github.com/aws-amplify/amplify-cli/compare/amplify-provider-awscloudformation@4.60.0...amplify-provider-awscloudformation@4.60.1) (2021-09-18) **Note:** Version bump only for package amplify-provider-awscloudformation diff --git a/packages/amplify-provider-awscloudformation/package.json b/packages/amplify-provider-awscloudformation/package.json index 0b8f55856a3..f16a53f5872 100644 --- a/packages/amplify-provider-awscloudformation/package.json +++ b/packages/amplify-provider-awscloudformation/package.json @@ -1,6 +1,6 @@ { "name": "amplify-provider-awscloudformation", - "version": "4.60.1", + "version": "4.61.0-graphql-vnext-dev-preview.0", "description": "AWS CloudFormation Provider", "repository": { "type": "git", @@ -24,15 +24,16 @@ "watch": "tsc --watch" }, "dependencies": { - "@aws-amplify/graphql-function-transformer": "0.4.3", - "@aws-amplify/graphql-http-transformer": "0.5.3", - "@aws-amplify/graphql-index-transformer": "0.3.2", - "@aws-amplify/graphql-model-transformer": "0.6.2", - "@aws-amplify/graphql-predictions-transformer": "0.3.3", - "@aws-amplify/graphql-relational-transformer": "0.2.1", - "@aws-amplify/graphql-searchable-transformer": "0.6.0", - "@aws-amplify/graphql-transformer-core": "0.9.0", - "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-auth-transformer": "0.2.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-function-transformer": "0.5.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-http-transformer": "0.5.4-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-index-transformer": "0.4.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-model-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-predictions-transformer": "0.4.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-relational-transformer": "0.3.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-searchable-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@aws-cdk/assets": "~1.124.0", "@aws-cdk/aws-apigatewayv2": "~1.124.0", "@aws-cdk/aws-autoscaling": "~1.124.0", @@ -72,10 +73,10 @@ "@aws-cdk/custom-resources": "~1.124.0", "@aws-cdk/region-info": "~1.124.0", "@octokit/rest": "^18.0.9", - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-cli-logger": "1.1.0", "amplify-codegen": "^2.23.1", - "amplify-util-import": "1.5.12", + "amplify-util-import": "1.5.13-graphql-vnext-dev-preview.0", "archiver": "^5.3.0", "aws-sdk": "^2.963.0", "bottleneck": "2.19.5", @@ -92,16 +93,16 @@ "fs-extra": "^8.1.0", "glob": "^7.1.6", "graphql": "^14.5.8", - "graphql-auth-transformer": "6.24.22", - "graphql-connection-transformer": "4.21.22", - "graphql-dynamodb-transformer": "6.22.22", - "graphql-elasticsearch-transformer": "4.12.1", - "graphql-function-transformer": "2.5.21", - "graphql-http-transformer": "4.18.9", - "graphql-key-transformer": "2.23.22", - "graphql-predictions-transformer": "2.5.21", - "graphql-transformer-core": "6.29.7", - "graphql-versioned-transformer": "4.17.22", + "graphql-auth-transformer": "6.25.0-graphql-vnext-dev-preview.0", + "graphql-connection-transformer": "4.21.23-graphql-vnext-dev-preview.0", + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0", + "graphql-elasticsearch-transformer": "4.12.2-graphql-vnext-dev-preview.0", + "graphql-function-transformer": "2.5.22-graphql-vnext-dev-preview.0", + "graphql-http-transformer": "4.18.10-graphql-vnext-dev-preview.0", + "graphql-key-transformer": "2.23.23-graphql-vnext-dev-preview.0", + "graphql-predictions-transformer": "2.5.22-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", + "graphql-versioned-transformer": "4.17.23-graphql-vnext-dev-preview.0", "ignore": "^5.1.8", "import-from": "^3.0.0", "import-global": "^0.1.0", diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/__snapshots__/utils.test.ts.snap b/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/__snapshots__/utils.test.ts.snap deleted file mode 100644 index 6ae1c362769..00000000000 --- a/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/__snapshots__/utils.test.ts.snap +++ /dev/null @@ -1,114 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`generateIterativeFuncDeploymentSteps generates steps with correct pointers 1`] = ` -Object { - "deploymentSteps": Array [ - Object { - "deployment": Object { - "capabilities": Array [], - "parameters": Object { - "param1": "value1", - }, - "previousMetaKey": undefined, - "stackName": "testStackId", - "stackTemplatePathOrUrl": "amplify-cfn-templates/function/temp/temp-func1-cloudformation-template.json", - "tableNames": Array [], - }, - "rollback": undefined, - }, - Object { - "deployment": Object { - "capabilities": Array [], - "parameters": Object { - "param2": "value2", - }, - "previousMetaKey": "amplify-cfn-templates/function/temp/temp-func1-deployment-meta.json", - "stackName": "testStackId", - "stackTemplatePathOrUrl": "amplify-cfn-templates/function/temp/temp-func2-cloudformation-template.json", - "tableNames": Array [], - }, - "rollback": Object { - "capabilities": Array [], - "parameters": Object { - "param1": "value1", - }, - "previousMetaKey": undefined, - "stackName": "testStackId", - "stackTemplatePathOrUrl": "amplify-cfn-templates/function/temp/temp-func1-cloudformation-template.json", - "tableNames": Array [], - }, - }, - ], - "lastMetaKey": "amplify-cfn-templates/function/temp/temp-func2-deployment-meta.json", -} -`; - -exports[`generateTempFuncCFNTemplates replaces Fn::ImportValue references with placeholder values in template 1`] = ` -Object { - "a": Object { - "b": Object { - "c": Array [ - Object { - "Fn::ImportValue": undefined, - "Fn::Sub": "TemporaryPlaceholderValue", - }, - Object { - "Fn::Join": Array [ - ":", - Object { - "Fn::ImportValue": undefined, - "Fn::Sub": "TemporaryPlaceholderValue", - }, - ], - }, - ], - }, - "d": Object { - "Fn::ImportValue": undefined, - "Fn::Sub": "TemporaryPlaceholderValue", - }, - }, -} -`; - -exports[`prependDeploymentSteps concatenates arrays and moves pointers appropriately 1`] = ` -Array [ - Object { - "deployment": Object { - "previousMetaKey": undefined, - "stackTemplatePathOrUrl": "deploymentStep1", - }, - "rollback": undefined, - }, - Object { - "deployment": Object { - "previousMetaKey": "deploymentStep1MetaKey", - "stackTemplatePathOrUrl": "deploymentStep2", - }, - "rollback": Object { - "previousMetaKey": undefined, - "stackTemplatePathOrUrl": "deploymentStep1", - }, - }, - Object { - "deployment": Object { - "previousMetaKey": "deploymentStep2MetaKey", - "stackTemplatePathOrUrl": "deploymentStep3", - }, - "rollback": Object { - "previousMetaKey": "deploymentStep1MetaKey", - "stackTemplatePathOrUrl": "deploymentStep2", - }, - }, - Object { - "deployment": Object { - "previousMetaKey": "deploymentStep3MetaKey", - "stackTemplatePathOrUrl": "deploymentStep4", - }, - "rollback": Object { - "previousMetaKey": "deploymentStep2MetaKey", - "stackTemplatePathOrUrl": "deploymentStep3", - }, - }, -] -`; diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts deleted file mode 100644 index 9b16d26be34..00000000000 --- a/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { - generateIterativeFuncDeploymentSteps, - generateTempFuncCFNTemplates, - getDependentFunctions, - prependDeploymentSteps, - uploadTempFuncDeploymentFiles, -} from '../../disconnect-dependent-resources/utils'; -import { pathManager, stateManager, readCFNTemplate, writeCFNTemplate, CFNTemplateFormat } from 'amplify-cli-core'; -import * as fs from 'fs-extra'; -import { S3 } from '../../aws-utils/aws-s3'; -import { CloudFormation } from 'aws-sdk'; -import { getPreviousDeploymentRecord } from '../../utils/amplify-resource-state-utils'; -import Template from 'cloudform-types/types/template'; -import { DeploymentOp, DeploymentStep } from '../../iterative-deployment'; - -jest.mock('fs-extra'); -jest.mock('amplify-cli-core'); -jest.mock('amplify-cli-logger'); -jest.mock('../../utils/amplify-resource-state-utils'); - -const fs_mock = fs as jest.Mocked; -const pathManager_mock = pathManager as jest.Mocked; -const stateManager_mock = stateManager as jest.Mocked; -const readCFNTemplate_mock = readCFNTemplate as jest.MockedFunction; -const writeCFNTemplate_mock = writeCFNTemplate as jest.MockedFunction; - -const getPreviousDeploymentRecord_mock = getPreviousDeploymentRecord as jest.MockedFunction; - -pathManager_mock.getResourceDirectoryPath.mockReturnValue('mock/path'); - -beforeEach(jest.clearAllMocks); - -describe('getDependentFunctions', () => { - it('returns the subset of functions that have a dependency on the models', async () => { - const func1Params = { - permissions: { - storage: { - someOtherTable: {}, - }, - }, - }; - const func2Params = { - permissions: { - storage: { - ['ModelName:@model(appsync)']: {}, - }, - }, - }; - const funcParamsSupplier = jest.fn().mockReturnValueOnce(func1Params).mockReturnValueOnce(func2Params); - const result = await getDependentFunctions(['ModelName', 'OtherModel'], ['func1', 'func2'], funcParamsSupplier); - expect(result).toEqual(['func2']); - }); -}); - -describe('generateTempFuncCFNTemplates', () => { - readCFNTemplate_mock.mockResolvedValueOnce({ - cfnTemplate: { - a: { - b: { - c: [ - { - 'Fn::ImportValue': { - 'Fn::Sub': 'test string', - }, - }, - { - 'Fn::Join': [ - ':', - { - 'Fn::ImportValue': 'testvalue', - }, - ], - }, - ], - }, - d: { - 'Fn::ImportValue': 'something else', - }, - }, - } as Template, - templateFormat: CFNTemplateFormat.JSON, - }); - it('replaces Fn::ImportValue references with placeholder values in template', async () => { - await generateTempFuncCFNTemplates(['func1']); - expect(writeCFNTemplate_mock.mock.calls[0][0]).toMatchSnapshot(); - }); -}); - -describe('uploadTempFuncDeploymentFiles', () => { - it('uploads template and meta file', async () => { - fs_mock.createReadStream - .mockReturnValueOnce('func1Template' as any) - .mockReturnValueOnce('func1Meta' as any) - .mockReturnValueOnce('func2Template' as any) - .mockReturnValueOnce('func2Meta' as any); - const s3Client_stub = { - uploadFile: jest.fn(), - }; - - await uploadTempFuncDeploymentFiles(s3Client_stub as unknown as S3, ['func1', 'func2']); - expect(s3Client_stub.uploadFile.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "Body": "func1Template", - "Key": "amplify-cfn-templates/function/temp/temp-func1-cloudformation-template.json", - }, - false, - ], - Array [ - Object { - "Body": "func1Meta", - "Key": "amplify-cfn-templates/function/temp/temp-func1-deployment-meta.json", - }, - false, - ], - Array [ - Object { - "Body": "func2Template", - "Key": "amplify-cfn-templates/function/temp/temp-func2-cloudformation-template.json", - }, - false, - ], - Array [ - Object { - "Body": "func2Meta", - "Key": "amplify-cfn-templates/function/temp/temp-func2-deployment-meta.json", - }, - false, - ], - ] - `); - }); - - it('logs and throws upload error', async () => { - const s3Client_stub = { - uploadFile: jest.fn().mockRejectedValue(new Error('test error')), - }; - try { - await uploadTempFuncDeploymentFiles(s3Client_stub as unknown as S3, ['func1', 'func2']); - fail('function call should error'); - } catch (err) { - expect(err.message).toMatchInlineSnapshot(`"test error"`); - } - }); -}); - -describe('generateIterativeFuncDeploymentSteps', () => { - it('generates steps with correct pointers', async () => { - const cfnClient_stub = { - describeStackResources: () => ({ - promise: async () => ({ - StackResources: [ - { - PhysicalResourceId: 'testStackId', - }, - ], - }), - }), - }; - getPreviousDeploymentRecord_mock - .mockResolvedValueOnce({ - parameters: { - param1: 'value1', - }, - capabilities: [], - }) - .mockResolvedValueOnce({ - parameters: { - param2: 'value2', - }, - capabilities: [], - }); - stateManager_mock.getResourceParametersJson.mockReturnValue({}); - stateManager_mock.getTeamProviderInfo.mockReturnValue({}); - stateManager_mock.getLocalEnvInfo.mockReturnValue({ envName: 'testenv' }); - const result = await generateIterativeFuncDeploymentSteps(cfnClient_stub as unknown as CloudFormation, 'testRootStackId', [ - 'func1', - 'func2', - ]); - expect(result).toMatchSnapshot(); - }); -}); - -describe('prependDeploymentSteps', () => { - it('concatenates arrays and moves pointers appropriately', () => { - const beforeSteps: DeploymentStep[] = [ - { - deployment: { - stackTemplatePathOrUrl: 'deploymentStep1', - previousMetaKey: undefined, - } as DeploymentOp, - rollback: undefined, - }, - { - deployment: { - stackTemplatePathOrUrl: 'deploymentStep2', - previousMetaKey: 'deploymentStep1MetaKey', - } as DeploymentOp, - rollback: { - stackTemplatePathOrUrl: 'deploymentStep1', - previousMetaKey: undefined, - } as DeploymentOp, - }, - ]; - - const afterSteps: DeploymentStep[] = [ - { - deployment: { - stackTemplatePathOrUrl: 'deploymentStep3', - previousMetaKey: undefined, - } as DeploymentOp, - rollback: undefined, - }, - { - deployment: { - stackTemplatePathOrUrl: 'deploymentStep4', - previousMetaKey: 'deploymentStep3MetaKey', - } as DeploymentOp, - rollback: { - stackTemplatePathOrUrl: 'deploymentStep3', - previousMetaKey: undefined, - } as DeploymentOp, - }, - ]; - - const result = prependDeploymentSteps(beforeSteps, afterSteps, 'deploymentStep2MetaKey'); - expect(result).toMatchSnapshot(); - }); - - it('returns after array if before array is empty', () => { - const afterSteps = ['test step' as unknown as DeploymentStep]; - const result = prependDeploymentSteps([], afterSteps, 'testmetakey'); - expect(result).toEqual(afterSteps); - }); -}); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/utils/api-key-helpers.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/utils/api-key-helpers.test.ts new file mode 100644 index 00000000000..ac142f3421f --- /dev/null +++ b/packages/amplify-provider-awscloudformation/src/__tests__/utils/api-key-helpers.test.ts @@ -0,0 +1,91 @@ +import { getAppSyncApiConfig, getApiKeyConfig, apiKeyIsActive, hasApiKey } from '../../utils/api-key-helpers'; + +jest.mock('amplify-cli-core', () => { + const original = jest.requireActual('amplify-cli-core'); + const amplifyMeta = { + api: { + myapp: { + service: 'AppSync', + output: { + authConfig: { + defaultAuthentication: { + authenticationType: 'AWS_IAM', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'API_KEY', + apiKeyConfig: { + apiKeyExpirationDays: 2, + apiKeyExpirationDate: '2021-08-20T20:38:07.585Z', + description: '', + }, + }, + ], + }, + }, + }, + }, + }; + + return { + ...original, + stateManager: { + getMeta: jest.fn().mockImplementation(() => amplifyMeta), + }, + }; +}); + +describe('getAppSyncApiConfig', () => { + it('returns the api object', async () => { + const result = getAppSyncApiConfig(); + + expect(result).toStrictEqual({ + service: 'AppSync', + output: { + authConfig: { + defaultAuthentication: { + authenticationType: 'AWS_IAM', + }, + additionalAuthenticationProviders: [ + { + authenticationType: 'API_KEY', + apiKeyConfig: { + apiKeyExpirationDays: 2, + apiKeyExpirationDate: '2021-08-20T20:38:07.585Z', + description: '', + }, + }, + ], + }, + }, + }); + }); +}); + +describe('getApiKeyConfig', () => { + it('returns the api key config', () => { + const result = getApiKeyConfig(); + + expect(result).toStrictEqual({ + apiKeyExpirationDays: 2, + apiKeyExpirationDate: '2021-08-20T20:38:07.585Z', + description: '', + }); + }); +}); + +describe('apiKeyIsActive', () => { + describe('with expired key', () => { + it('returns false', () => { + expect(apiKeyIsActive()).toBe(false); + }); + }); +}); + +describe('hasApiKey', () => { + describe('if api key config is present', () => { + it('returns true if api key is present', () => { + expect(hasApiKey()).toBe(true); + }); + }); +}); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/utils/sandbox-mode-helpers.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/utils/sandbox-mode-helpers.test.ts new file mode 100644 index 00000000000..184bf7df594 --- /dev/null +++ b/packages/amplify-provider-awscloudformation/src/__tests__/utils/sandbox-mode-helpers.test.ts @@ -0,0 +1,127 @@ +import { showSandboxModePrompts, removeSandboxDirectiveFromSchema } from '../../utils/sandbox-mode-helpers'; +import { $TSContext } from 'amplify-cli-core'; +import chalk from 'chalk'; +import * as prompts from 'amplify-prompts'; +import * as apiKeyHelpers from '../../utils/api-key-helpers'; + +let ctx; +let apiKeyActive = true; +let apiKeyPresent = true; + +describe('sandbox mode helpers', () => { + beforeEach(() => { + const envName = 'dev'; + ctx = { + amplify: { + getEnvInfo() { + return { envName }; + }, + invokePluginMethod: jest.fn(), + }, + } as unknown as $TSContext; + + jest.spyOn(prompts.printer, 'info').mockImplementation(); + jest.spyOn(apiKeyHelpers, 'apiKeyIsActive').mockReturnValue(apiKeyActive); + jest.spyOn(apiKeyHelpers, 'hasApiKey').mockReturnValue(apiKeyPresent); + }); + + describe('showSandboxModePrompts', () => { + describe('missing api key', () => { + beforeAll(() => { + apiKeyPresent = false; + }); + + it('displays warning', async () => { + await showSandboxModePrompts(ctx); + + expect(prompts.printer.info).toBeCalledWith( + ` +⚠️ WARNING: Global Sandbox Mode has been enabled, which requires a valid API key. If +you'd like to disable, remove ${chalk.green('"type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key"')} +from your GraphQL schema and run 'amplify push' again. If you'd like to proceed with +sandbox mode disabled in '${ctx.amplify.getEnvInfo().envName}', do not create an API Key. +`, + 'yellow', + ); + expect(ctx.amplify.invokePluginMethod).toBeCalledWith(ctx, 'api', undefined, 'promptToAddApiKey', [ctx]); + }); + }); + + describe('expired api key', () => { + beforeAll(() => { + apiKeyActive = false; + }); + + it('displays warning', async () => { + await showSandboxModePrompts(ctx); + + expect(prompts.printer.info).toBeCalledWith( + ` +⚠️ WARNING: Global Sandbox Mode has been enabled, which requires a valid API key. If +you'd like to disable, remove ${chalk.green('"type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key"')} +from your GraphQL schema and run 'amplify push' again. If you'd like to proceed with +sandbox mode disabled in '${ctx.amplify.getEnvInfo().envName}', do not create an API Key. +`, + 'yellow', + ); + expect(ctx.amplify.invokePluginMethod).toBeCalledWith(ctx, 'api', undefined, 'promptToAddApiKey', [ctx]); + }); + }); + }); + + describe('removeSandboxDirectiveFromSchema', () => { + it('removes sandbox mode directive', () => { + const schema = ` +type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: "dev") + `; + + expect(removeSandboxDirectiveFromSchema(schema)).toEqual(` + + `); + }); + + it('does not change user schema with directive', () => { + const schema = ` +type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: "dev10105") # FOR TESTING ONLY! + +type Todo @model { + id: ID! + name: String! + description: String +} + `; + + expect(removeSandboxDirectiveFromSchema(schema)).toEqual(` + # FOR TESTING ONLY! + +type Todo @model { + id: ID! + name: String! + description: String +} + `); + }); + + it('does not change user schema with directive and single quotes', () => { + const schema = ` +type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: 'dev10105') # FOR TESTING ONLY! + +type Todo @model { + id: ID! + name: String! + description: String +} + `; + + expect(removeSandboxDirectiveFromSchema(schema)).toEqual(` + # FOR TESTING ONLY! + +type Todo @model { + id: ID! + name: String! + description: String +} + `); + }); + }); +}); diff --git a/packages/amplify-provider-awscloudformation/src/constants.js b/packages/amplify-provider-awscloudformation/src/constants.js index 4b6232b492c..d05deef8387 100644 --- a/packages/amplify-provider-awscloudformation/src/constants.js +++ b/packages/amplify-provider-awscloudformation/src/constants.js @@ -15,5 +15,4 @@ module.exports = { FunctionCategoryName: 'function', // keep in sync with ServiceName in amplify-category-function, but probably it will not change FunctionServiceNameLambdaLayer: 'LambdaLayer', - destructiveUpdatesFlag: 'allow-destructive-graphql-schema-updates', }; diff --git a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts b/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts deleted file mode 100644 index 6fdae96a7f9..00000000000 --- a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { $TSAny, $TSContext, pathManager, stateManager } from 'amplify-cli-core'; -import { CloudFormation } from 'aws-sdk'; -import { S3 } from '../aws-utils/aws-s3'; -import { loadConfiguration } from '../configuration-manager'; -import { DeploymentStep } from '../iterative-deployment'; -import { - getDependentFunctions, - generateIterativeFuncDeploymentSteps, - prependDeploymentSteps, - generateTempFuncCFNTemplates, - uploadTempFuncDeploymentFiles, - s3Prefix, - localPrefix, -} from './utils'; -import * as fs from 'fs-extra'; - -let functionsDependentOnReplacedModelTables: string[] = []; - -/** - * Identifies if any functions depend on a model table that is being replaced. - * If so, it creates temporary CFN templates for these functions that do not reference the replaced table and adds deployment steps to the array to update the functions before the table is replaced - * @param context Amplify context - * @param modelsBeingReplaced Names of the models being replaced during this push operation - * @param deploymentSteps The existing list of deployment steps that will be prepended to in the case of dependent functions - * @returns The new list of deploymentSteps - */ -export const prependDeploymentStepsToDisconnectFunctionsFromReplacedModelTables = async ( - context: $TSContext, - modelsBeingReplaced: string[], - deploymentSteps: DeploymentStep[], -): Promise => { - const amplifyMeta = stateManager.getMeta(); - const rootStackId = amplifyMeta?.providers?.awscloudformation?.StackId; - const allFunctionNames = Object.keys(amplifyMeta?.function); - functionsDependentOnReplacedModelTables = await getDependentFunctions( - modelsBeingReplaced, - allFunctionNames, - getFunctionParamsSupplier(context), - ); - // generate deployment steps that will remove references to the replaced tables in the dependent functions - const { deploymentSteps: disconnectFuncsSteps, lastMetaKey } = await generateIterativeFuncDeploymentSteps( - new CloudFormation(await loadConfiguration(context)), - rootStackId, - functionsDependentOnReplacedModelTables, - ); - await generateTempFuncCFNTemplates(functionsDependentOnReplacedModelTables); - await uploadTempFuncDeploymentFiles(await S3.getInstance(context), functionsDependentOnReplacedModelTables); - return prependDeploymentSteps(disconnectFuncsSteps, deploymentSteps, lastMetaKey); -}; - -export const postDeploymentCleanup = async (s3Client: S3, deploymentBucketName: string) => { - if (functionsDependentOnReplacedModelTables.length < 1) { - return; - } - await s3Client.deleteDirectory(deploymentBucketName, s3Prefix); - await Promise.all(functionsDependentOnReplacedModelTables.map(funcName => fs.remove(localPrefix(funcName)))); -}; - -// helper function to load the function-parameters.json file given a functionName -const getFunctionParamsSupplier = (context: $TSContext) => async (functionName: string) => { - return context.amplify.invokePluginMethod(context, 'function', undefined, 'loadFunctionParameters', [ - pathManager.getResourceDirectoryPath(undefined, 'function', functionName), - ]) as $TSAny; -}; diff --git a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts b/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts deleted file mode 100644 index e91ede4168f..00000000000 --- a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { $TSAny, JSONUtilities, pathManager, readCFNTemplate, stateManager, writeCFNTemplate } from 'amplify-cli-core'; -import * as path from 'path'; -import * as fs from 'fs-extra'; -import { S3 } from '../aws-utils/aws-s3'; -import { fileLogger } from '../utils/aws-logger'; -import { CloudFormation } from 'aws-sdk'; -import { getPreviousDeploymentRecord } from '../utils/amplify-resource-state-utils'; -import { DeploymentOp, DeploymentStep } from '../iterative-deployment'; -import _ from 'lodash'; - -const logger = fileLogger('disconnect-dependent-resources'); - -/** - * Returns the subset of functionNames that have a dependency on a model in modelNames - */ -export const getDependentFunctions = async ( - modelNames: string[], - functionNames: string[], - functionParamsSupplier: (functionName: string) => Promise<$TSAny>, -) => { - const dependentFunctions: string[] = []; - for (const funcName of functionNames) { - const funcParams = await functionParamsSupplier(funcName); - const dependentModels = funcParamsToDependentAppSyncModels(funcParams); - const hasDep = dependentModels.map(model => modelNames.includes(model)).reduce((acc, it) => acc || it, false); - if (hasDep) { - dependentFunctions.push(funcName); - } - } - return dependentFunctions; -}; - -/** - * Generates temporary CFN templates for the given functions that have placeholder values for all references to replaced model tables - */ -export const generateTempFuncCFNTemplates = async (dependentFunctions: string[]) => { - const tempPaths: string[] = []; - for (const funcName of dependentFunctions) { - const { cfnTemplate, templateFormat } = await readCFNTemplate( - path.join(pathManager.getResourceDirectoryPath(undefined, 'function', funcName), `${funcName}-cloudformation-template.json`), - ); - replaceFnImport(cfnTemplate); - const tempPath = getTempFuncTemplateLocalPath(funcName); - await writeCFNTemplate(cfnTemplate, tempPath, { templateFormat }); - tempPaths.push(tempPath); - } -}; - -/** - * Uploads the CFN template and iterative deployment meta file to S3 - */ -export const uploadTempFuncDeploymentFiles = async (s3Client: S3, funcNames: string[]) => { - for (const funcName of funcNames) { - const uploads = [ - { - Body: fs.createReadStream(getTempFuncTemplateLocalPath(funcName)), - Key: getTempFuncTemplateS3Key(funcName), - }, - { - Body: fs.createReadStream(getTempFuncMetaLocalPath(funcName)), - Key: getTempFuncMetaS3Key(funcName), - }, - ]; - const log = logger('uploadTemplateToS3.s3.uploadFile', [{ Key: uploads[0].Key }]); - for (const upload of uploads) { - try { - await s3Client.uploadFile(upload, false); - } catch (error) { - log(error); - throw error; - } - } - } -}; - -export const generateIterativeFuncDeploymentSteps = async ( - cfnClient: CloudFormation, - rootStackId: string, - functionNames: string[], -): Promise<{ deploymentSteps: DeploymentStep[]; lastMetaKey: string }> => { - let rollback: DeploymentOp; - let previousMetaKey: string; - const steps: DeploymentStep[] = []; - for (const funcName of functionNames) { - const deploymentOp = await generateIterativeFuncDeploymentOp(cfnClient, rootStackId, funcName); - deploymentOp.previousMetaKey = previousMetaKey; - steps.push({ - deployment: deploymentOp, - rollback, - }); - rollback = deploymentOp; - previousMetaKey = getTempFuncMetaS3Key(funcName); - } - return { deploymentSteps: steps, lastMetaKey: previousMetaKey }; -}; - -/** - * Prepends beforeSteps and afterSteps into a single array of deployment steps. - * Moves rollback and previousMetaKey pointers to maintain the integrity of the deployment steps. - */ -export const prependDeploymentSteps = (beforeSteps: DeploymentStep[], afterSteps: DeploymentStep[], beforeStepsLastMetaKey: string) => { - if (beforeSteps.length === 0) { - return afterSteps; - } - beforeSteps[0].rollback = _.cloneDeep(afterSteps[0].rollback); - beforeSteps[0].deployment.previousMetaKey = afterSteps[0].deployment.previousMetaKey; - afterSteps[0].rollback = _.cloneDeep(beforeSteps[beforeSteps.length - 1].deployment); - afterSteps[0].deployment.previousMetaKey = beforeStepsLastMetaKey; - if (afterSteps.length > 1) { - afterSteps[1].rollback.previousMetaKey = beforeStepsLastMetaKey; - } - return beforeSteps.concat(afterSteps); -}; - -/** - * Generates a deployment operation for a temporary function deployment. - * Also writes the deployment operation to the temp meta path - */ -const generateIterativeFuncDeploymentOp = async (cfnClient: CloudFormation, rootStackId: string, functionName: string) => { - const funcStack = await cfnClient - .describeStackResources({ StackName: rootStackId, LogicalResourceId: `function${functionName}` }) - .promise(); - const funcStackId = funcStack.StackResources[0].PhysicalResourceId; - const { parameters, capabilities } = await getPreviousDeploymentRecord(cfnClient, funcStackId); - const funcCfnParams = stateManager.getResourceParametersJson(undefined, 'function', functionName, { - throwIfNotExist: false, - default: {}, - }); - const tpi = stateManager.getTeamProviderInfo(undefined, { throwIfNotExist: false, default: {} }); - const env = stateManager.getLocalEnvInfo().envName; - const tpiCfnParams = tpi?.[env]?.categories?.function?.[functionName] || {}; - const params = { ...parameters, ...funcCfnParams, ...tpiCfnParams }; - const deploymentStep: DeploymentOp = { - stackTemplatePathOrUrl: getTempFuncTemplateS3Key(functionName), - parameters: params, - stackName: funcStackId, - capabilities, - tableNames: [], - }; - - JSONUtilities.writeJson(getTempFuncMetaLocalPath(functionName), deploymentStep); - return deploymentStep; -}; - -// helper functions for constructing local paths and S3 keys for function templates and deployment meta files -const getTempFuncTemplateS3Key = (funcName: string): string => path.posix.join(s3Prefix, tempTemplateFilename(funcName)); -const getTempFuncTemplateLocalPath = (funcName: string): string => path.join(localPrefix(funcName), tempTemplateFilename(funcName)); -const getTempFuncMetaLocalPath = (funcName: string): string => path.join(localPrefix(funcName), tempMetaFilename(funcName)); -const getTempFuncMetaS3Key = (funcName: string): string => path.posix.join(s3Prefix, tempMetaFilename(funcName)); - -const tempTemplateFilename = (funcName: string) => `temp-${funcName}-cloudformation-template.json`; -const tempMetaFilename = (funcName: string) => `temp-${funcName}-deployment-meta.json`; -export const s3Prefix = 'amplify-cfn-templates/function/temp'; -export const localPrefix = funcName => path.join(pathManager.getResourceDirectoryPath(undefined, 'function', funcName), 'temp'); - -/** - * Recursively searches for 'Fn::ImportValue' nodes in a CFN template object and replaces them with a placeholder value - * @param node - * @returns - */ -const replaceFnImport = (node: $TSAny) => { - if (typeof node !== 'object') { - return; - } - if (Array.isArray(node)) { - node.forEach(el => replaceFnImport(el)); - } - const nodeKeys = Object.keys(node); - if (nodeKeys.length === 1 && nodeKeys[0] === 'Fn::ImportValue') { - node['Fn::ImportValue'] = undefined; - node['Fn::Sub'] = 'TemporaryPlaceholderValue'; - return; - } - Object.values(node).forEach(value => replaceFnImport(value)); -}; - -/** - * Given the contents of the function-parameters.json file for a function, returns the list of AppSync models this function depends on. - */ -const funcParamsToDependentAppSyncModels = (funcParams: $TSAny): string[] => - Object.keys(funcParams?.permissions?.storage || {}) - .filter(key => key.endsWith(':@model(appsync)')) - .map(key => key.slice(0, key.lastIndexOf(':'))); diff --git a/packages/amplify-provider-awscloudformation/src/graphql-transformer/amplify-graphql-resource-manager.ts b/packages/amplify-provider-awscloudformation/src/graphql-transformer/amplify-graphql-resource-manager.ts index aaa40b429b7..cd82d286df8 100644 --- a/packages/amplify-provider-awscloudformation/src/graphql-transformer/amplify-graphql-resource-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/graphql-transformer/amplify-graphql-resource-manager.ts @@ -26,7 +26,6 @@ export type GQLResourceManagerProps = { resourceMeta?: ResourceMeta; backendDir: string; cloudBackendDir: string; - rebuildAllTables?: boolean; }; export type ResourceMeta = { @@ -53,9 +52,8 @@ export class GraphQLResourceManager { private cloudBackendApiProjectRoot: string; private backendApiProjectRoot: string; private templateState: TemplateState; - private rebuildAllTables: boolean = false; // indicates that all underlying model tables should be rebuilt - public static createInstance = async (context: $TSContext, gqlResource: any, StackId: string, rebuildAllTables: boolean = false) => { + public static createInstance = async (context: $TSContext, gqlResource: any, StackId: string) => { try { const cred = await loadConfiguration(context); const cfn = new CloudFormation(cred); @@ -67,7 +65,6 @@ export class GraphQLResourceManager { resourceMeta: { ...gqlResource, stackId: apiStack.StackResources[0].PhysicalResourceId }, backendDir: pathManager.getBackendDirPath(), cloudBackendDir: pathManager.getCurrentCloudBackendDirPath(), - rebuildAllTables, }); } catch (err) { throw err; @@ -85,7 +82,6 @@ export class GraphQLResourceManager { this.backendApiProjectRoot = path.join(props.backendDir, GraphQLResourceManager.categoryName, this.resourceMeta.resourceName); this.cloudBackendApiProjectRoot = path.join(props.cloudBackendDir, GraphQLResourceManager.categoryName, this.resourceMeta.resourceName); this.templateState = new TemplateState(); - this.rebuildAllTables = props.rebuildAllTables || false; } run = async (): Promise => { @@ -106,10 +102,7 @@ export class GraphQLResourceManager { throw err; } } - if (!this.rebuildAllTables) { - this.gsiManagement(gqlDiff.diff, gqlDiff.current, gqlDiff.next); - } - this.tableRecreationManagement(gqlDiff.current, gqlDiff.next); + this.gsiManagement(gqlDiff.diff, gqlDiff.current, gqlDiff.next); return await this.getDeploymentSteps(); }; @@ -224,9 +217,9 @@ export class GraphQLResourceManager { }); const tableWithGSIChanges = _.uniqBy(gsiChanges, diff => diff.path?.slice(0, 3).join('/')).map(gsiChange => { - const tableName = gsiChange.path[3] as string; + const tableName = gsiChange.path[3]; - const stackName = gsiChange.path[1].split('.')[0] as string; + const stackName = gsiChange.path[1].split('.')[0]; const currentTable = this.getTable(gsiChange, currentState); const nextTable = this.getTable(gsiChange, nextState); @@ -273,41 +266,6 @@ export class GraphQLResourceManager { } }; - private tableRecreationManagement = (currentState: DiffableProject, nextState: DiffableProject) => { - this.getTablesBeingReplaced().forEach(tableMeta => { - const ddbResource = this.getStack(tableMeta.stackName, currentState); - this.dropTable(tableMeta.tableName, ddbResource); - // clear any other states created by GSI updates as dropping and recreating supercedes those changes - this.clearTemplateState(tableMeta.stackName); - this.templateState.add(tableMeta.stackName, JSONUtilities.stringify(ddbResource)); - this.templateState.add(tableMeta.stackName, JSONUtilities.stringify(this.getStack(tableMeta.stackName, nextState))); - }); - }; - - getTablesBeingReplaced = () => { - const gqlDiff = getGQLDiff(this.backendApiProjectRoot, this.cloudBackendApiProjectRoot); - const [diffs, currentState] = [gqlDiff.diff, gqlDiff.current]; - const getTablesRequiringReplacement = () => - _.uniq( - diffs - .filter(diff => diff.path.includes('KeySchema') || diff.path.includes('LocalSecondaryIndexes')) // filter diffs with changes that require replacement - .map(diff => ({ - // extract table name and stack name from diff path - tableName: diff.path?.[3] as string, - stackName: diff.path[1].split('.')[0] as string, - })), - ) as { tableName: string; stackName: string }[]; - - const getAllTables = () => - Object.entries(currentState.stacks) - .map(([name, template]) => ({ - tableName: this.getTableNameFromTemplate(template), - stackName: path.basename(name, '.json'), - })) - .filter(meta => !!meta.tableName); - return this.rebuildAllTables ? getAllTables() : getTablesRequiringReplacement(); - }; - private getTable = (gsiChange: Diff, proj: DiffableProject): DynamoDB.Table => { return proj.stacks[gsiChange.path[1]].Resources[gsiChange.path[3]] as DynamoDB.Table; }; @@ -325,21 +283,6 @@ export class GraphQLResourceManager { const table = template.Resources[tableName] as DynamoDB.Table; template.Resources[tableName] = removeGSI(indexName, table); }; - - private dropTable = (tableName: string, template: Template): void => { - // remove table and all output refs to it - template.Resources[tableName] = undefined; - template.Outputs = _.omitBy(template.Outputs, (_, key) => key.includes(tableName)); - }; - - private clearTemplateState = (stackName: string) => { - while (this.templateState.has(stackName)) { - this.templateState.pop(stackName); - } - }; - - private getTableNameFromTemplate = (template: Template): string | undefined => - Object.entries(template?.Resources || {}).find(([_, resource]) => resource.Type === 'AWS::DynamoDB::Table')?.[0]; } // https://stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript diff --git a/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts b/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts index 9274dcad0a5..0f0638fd127 100644 --- a/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts +++ b/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts @@ -3,14 +3,16 @@ import path from 'path'; import importGlobal from 'import-global'; import { print } from 'graphql'; import importFrom from 'import-from'; -import { TransformerPluginProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { TransformerPluginProvider, AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; import { getAppSyncServiceExtraDirectives, GraphQLTransform, collectDirectivesByTypeNames, + collectDirectives, TransformerProjectConfig, } from '@aws-amplify/graphql-transformer-core'; import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; import { FunctionTransformer } from '@aws-amplify/graphql-function-transformer'; import { HttpTransformer } from '@aws-amplify/graphql-http-transformer'; import { PredictionsTransformer } from '@aws-amplify/graphql-predictions-transformer'; @@ -24,13 +26,18 @@ import { import { SearchableModelTransformer } from '@aws-amplify/graphql-searchable-transformer'; import { ProviderName as providerName } from '../constants'; import { hashDirectory } from '../upload-appsync-files'; -import { writeDeploymentToDisk } from './utils'; +import { showACM, writeDeploymentToDisk } from './utils'; import { loadProject as readTransformerConfiguration } from './transform-config'; import { loadProject } from 'graphql-transformer-core'; -import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-core'; import { Template } from '@aws-amplify/graphql-transformer-core/lib/config/project-config'; import { AmplifyCLIFeatureFlagAdapter } from '../utils/amplify-cli-feature-flag-adapter'; -import { JSONUtilities } from 'amplify-cli-core'; +import { isAmplifyAdminApp } from '../utils/admin-helpers'; +import { JSONUtilities, stateManager, $TSContext } from 'amplify-cli-core'; +import { + showSandboxModePrompts, + getSandboxModeEnvNameFromDirectiveSet, + removeSandboxDirectiveFromSchema, +} from '../utils/sandbox-mode-helpers'; const API_CATEGORY = 'api'; const STORAGE_CATEGORY = 'storage'; @@ -52,7 +59,10 @@ function warnOnAuth(context, map) { } } -function getTransformerFactory(context, resourceDir) { +function getTransformerFactory( + context: $TSContext, + resourceDir: string, +): (options: TransformerFactoryArgs) => Promise { return async (options?: TransformerFactoryArgs) => { const modelTransformer = new ModelTransformer(); const indexTransformer = new IndexTransformer(); @@ -143,7 +153,19 @@ function getTransformerFactory(context, resourceDir) { // TODO: Build dependency mechanism into transformers. Auth runs last // so any resolvers that need to be protected will already be created. - // transformerList.push(new ModelAuthTransformer({ authConfig })); + + let amplifyAdminEnabled: boolean = false; + let adminUserPoolID: string; + try { + const amplifyMeta = stateManager.getMeta(); + const appId = amplifyMeta?.providers?.[providerName]?.AmplifyAppId; + const res = await isAmplifyAdminApp(appId); + amplifyAdminEnabled = res.isAdminApp; + adminUserPoolID = res.userPoolID; + } catch (err) { + // Do Nothing + } + transformerList.push(new AuthTransformer({ authConfig: options?.authConfig, addAwsIamAuthInOutputSchema: false, adminUserPoolID })); return transformerList; }; @@ -220,7 +242,7 @@ export async function transformGraphQLSchema(context, options) { } } - let { authConfig } = options; + let { authConfig }: { authConfig: AppSyncAuthConfiguration } = options; // // If we don't have an authConfig from the caller, use it from the @@ -268,6 +290,11 @@ export async function transformGraphQLSchema(context, options) { const project = await loadProject(resourceDir); + if (flags['acm']) { + showACM(project.schema, flags['acm']); + return; + } + const lastDeployedProjectConfig = fs.existsSync(previouslyDeployedBackendDir) ? await loadProject(previouslyDeployedBackendDir) : undefined; @@ -278,6 +305,17 @@ export async function transformGraphQLSchema(context, options) { const transformerListFactory = getTransformerFactory(context, resourceDir); + const { envName } = context.amplify._getEnvInfo(); + const sandboxModeEnv = getSandboxModeEnvNameFromDirectiveSet(collectDirectives(project.schema)); + const sandboxModeEnabled = envName === sandboxModeEnv; + + if (sandboxModeEnabled && options.promptApiKeyCreation) { + const apiKeyConfig = await showSandboxModePrompts(context); + if (apiKeyConfig) { + authConfig.additionalAuthenticationProviders.push(apiKeyConfig); + } + } + let searchableTransformerFlag = false; if (directiveMap.directives.includes('searchable')) { @@ -289,17 +327,19 @@ export async function transformGraphQLSchema(context, options) { buildParameters, projectDirectory: resourceDir, transformersFactory: transformerListFactory, - transformersFactoryArgs: { addSearchableTransformer: searchableTransformerFlag, storageConfig }, + transformersFactoryArgs: { addSearchableTransformer: searchableTransformerFlag, storageConfig, authConfig }, rootStackFileName: 'cloudformation-template.json', currentCloudBackendDirectory: previouslyDeployedBackendDir, minify: options.minify, projectConfig: project, lastDeployedProjectConfig, authConfig, + sandboxModeEnabled, }; + const transformerOutput = await buildAPIProject(buildConfig); - context.print.success(`\nGraphQL schema compiled successfully.\n\nEdit your schema at ${schemaFilePath} or \ + context.print.success(`GraphQL schema compiled successfully.\n\nEdit your schema at ${schemaFilePath} or \ place .graphql files in a directory at ${schemaDirPath}`); if (!options.dryRun) { @@ -331,8 +371,8 @@ async function getPreviousDeploymentRootKey(previouslyDeployedBackendDir) { } } -export async function getDirectiveDefinitions(context, resourceDir) { - const transformList = await getTransformerFactory(context, resourceDir)({ addSearchableTransformer: true }); +export async function getDirectiveDefinitions(context: $TSContext, resourceDir: string) { + const transformList = await getTransformerFactory(context, resourceDir)({ addSearchableTransformer: true, authConfig: {} }); const appSynDirectives = getAppSyncServiceExtraDirectives(); const transformDirectives = transformList .map(transformPluginInst => [transformPluginInst.directive, ...transformPluginInst.typeDefinitions].map(node => print(node)).join('\n')) @@ -379,6 +419,7 @@ function getBucketName(context, s3ResourceName, backEndDir) { type TransformerFactoryArgs = { addSearchableTransformer: boolean; + authConfig: any; storageConfig?: any; }; export type ProjectOptions = { @@ -387,7 +428,7 @@ export type ProjectOptions = { S3DeploymentRootKey: string; }; projectDirectory?: string; - transformersFactory: (options: T) => TransformerPluginProvider[]; + transformersFactory: (options: T) => Promise; transformersFactoryArgs: T; rootStackFileName: 'cloudformation-template.json'; currentCloudBackendDirectory: string; @@ -397,6 +438,7 @@ export type ProjectOptions = { dryRun?: boolean; authConfig?: AppSyncAuthConfiguration; stacks: Record; + sandboxModeEnabled?: boolean; }; export async function buildAPIProject(opts: ProjectOptions) { @@ -414,6 +456,9 @@ export async function buildAPIProject(opts: ProjectOptions) { buildParameters: opts.buildParameters, stacks: opts.projectConfig.stacks || {}, featureFlags: new AmplifyCLIFeatureFlagAdapter(), + sandboxModeEnabled: opts.sandboxModeEnabled, }); - return transform.transform(userProjectConfig.schema.toString()); + + let schema = userProjectConfig.schema.toString(); + if (opts.sandboxModeEnabled) schema = removeSandboxDirectiveFromSchema(schema); + + return transform.transform(schema); } diff --git a/packages/amplify-provider-awscloudformation/src/graphql-transformer/utils.ts b/packages/amplify-provider-awscloudformation/src/graphql-transformer/utils.ts index fbbac527037..b26fab39135 100644 --- a/packages/amplify-provider-awscloudformation/src/graphql-transformer/utils.ts +++ b/packages/amplify-provider-awscloudformation/src/graphql-transformer/utils.ts @@ -1,10 +1,19 @@ import fs from 'fs-extra'; import * as path from 'path'; import { DeploymentResources } from '@aws-amplify/graphql-transformer-core'; +import { + AccessControlMatrix, + AuthRule, + ModelOperation, + MODEL_OPERATIONS, + DEFAULT_GROUPS_FIELD, + DEFAULT_OWNER_FIELD, +} from '@aws-amplify/graphql-auth-transformer'; import rimraf from 'rimraf'; import { JSONUtilities } from 'amplify-cli-core'; import { Template } from 'cloudform'; import { Diff, diff as getDiffs } from 'deep-diff'; +import { parse, ObjectTypeDefinitionNode, ArgumentNode, DirectiveNode, valueFromASTUntyped, FieldDefinitionNode } from 'graphql'; const ROOT_STACK_FILE_NAME = 'cloudformation-template.json'; const PARAMETERS_FILE_NAME = 'parameters.json'; @@ -34,11 +43,20 @@ export const getGQLDiff = (currentBackendDir: string, cloudBackendDir: string): return null; }; -export const getGqlUpdatedResource = (resources: any[]) => - resources.find( - resource => - resource?.service === 'AppSync' && resource?.providerMetadata?.logicalId && resource?.providerPlugin === 'awscloudformation', - ) || null; +export const getGqlUpdatedResource = (resources: any[]) => { + if (resources.length > 0) { + const resource = resources[0]; + if ( + resource.service === 'AppSync' && + resource.providerMetadata && + resource.providerMetadata.logicalId && + resource.providerPlugin === 'awscloudformation' + ) { + return resource; + } + } + return null; +}; export function loadDiffableProject(path: string, rootStackName: string): DiffableProject { const project = readFromPath(path); @@ -191,3 +209,127 @@ export function throwIfNotJSONExt(stackFile: string) { throw new Error(`Invalid extension ${extension} for stack ${stackFile}`); } } + +/* +Used only for @auth testing +given a model can output the truth table for a @model that has @auth + */ +export function showACM(sdl: string, nodeName: string) { + const schema = parse(sdl); + const type = schema.definitions.find( + node => node.kind === 'ObjectTypeDefinition' && node.name.value === nodeName && node.directives.find(dir => dir.name.value === 'model'), + ) as ObjectTypeDefinitionNode; + if (!type) { + throw new Error(`${nodeName} does not have @model`); + } else { + const fields: string[] = type.fields!.map((field: FieldDefinitionNode) => field.name.value); + const acm = new AccessControlMatrix({ operations: MODEL_OPERATIONS, resources: fields }); + const parentAuthDirective = type.directives?.find(dir => dir.name.value === 'auth'); + if (parentAuthDirective) { + const authRules = getAuthRulesFromDirective(parentAuthDirective); + ensureAuthRuleDefaults(authRules); + convertModelRulesToRoles(acm, authRules); + } + for (let fieldNode of type.fields || []) { + let fieldAuthDir = fieldNode.directives?.find(dir => dir.name.value === 'auth') as DirectiveNode; + if (fieldAuthDir) { + if (parentAuthDirective) { + acm.resetAccessForResource(fieldNode.name.value); + } + let fieldAuthRules = getAuthRulesFromDirective(fieldAuthDir); + ensureAuthRuleDefaults(fieldAuthRules); + convertModelRulesToRoles(acm, fieldAuthRules, fieldNode.name.value); + } + } + const truthTable = acm.getAcmPerRole(); + for (let [role, acm] of truthTable) { + console.group(role); + console.table(acm); + console.groupEnd(); + } + } +} +// helper functions for setting up acm +function getAuthRulesFromDirective(directive: DirectiveNode) { + const get = (s: string) => (arg: ArgumentNode) => arg.name.value === s; + const getArg = (arg: string, dflt?: any) => { + const argument = directive.arguments?.find(get(arg)); + return argument ? valueFromASTUntyped(argument.value) : dflt; + }; + + // Get and validate the auth rules. + const authRules = getArg('rules', []) as AuthRule[]; + + // All the IAM auth rules that are added using @auth directive need IAM policy to be generated. AuthRules added for AdminUI don't + return authRules.map(rule => (rule.provider === 'iam' ? { ...rule, generateIAMPolicy: true } : rule)); +} + +function ensureAuthRuleDefaults(rules: AuthRule[]) { + // We assign the default provider if an override is not present make further handling easier. + for (const rule of rules) { + if (!rule.provider) { + switch (rule.allow) { + case 'owner': + case 'groups': + rule.provider = 'userPools'; + break; + case 'private': + rule.provider = 'userPools'; + break; + case 'public': + rule.provider = 'apiKey'; + break; + case 'custom': + rule.provider = 'function'; + break; + default: + rule.provider = null; + break; + } + } + if (rule.provider === 'iam') { + rule.generateIAMPolicy = true; + } + } +} +function convertModelRulesToRoles(acm: AccessControlMatrix, authRules: AuthRule[], field?: string) { + for (let rule of authRules) { + let operations: ModelOperation[] = rule.operations || MODEL_OPERATIONS; + if (rule.groups && !rule.groupsField) { + rule.groups.forEach(group => { + let roleName = `${rule.provider}:staticGroup:${group}`; + acm.setRole({ role: roleName, resource: field, operations }); + }); + } else { + let roleName: string; + switch (rule.provider) { + case 'apiKey': + roleName = 'apiKey:public'; + break; + case 'function': + roleName = 'function:custom'; + break; + case 'iam': + roleName = `iam:${rule.allow}`; + break; + case 'oidc': + case 'userPools': + if (rule.allow === 'groups') { + let groupsField = rule.groupsField || DEFAULT_GROUPS_FIELD; + roleName = `${rule.provider}:dynamicGroup:${groupsField}`; + } else if (rule.allow === 'owner') { + let ownerField = rule.ownerField || DEFAULT_OWNER_FIELD; + roleName = `${rule.provider}:owner:${ownerField}`; + } else if (rule.allow === 'private') { + roleName = `${rule.provider}:${rule.allow}`; + } else { + throw new Error(`Could not create a role from ${JSON.stringify(rule)}`); + } + break; + default: + throw new Error(`Could not create a role from ${JSON.stringify(rule)}`); + } + acm.setRole({ role: roleName, resource: field, operations }); + } + } +} diff --git a/packages/amplify-provider-awscloudformation/src/index.ts b/packages/amplify-provider-awscloudformation/src/index.ts index 9574bd29f60..1c5e68714f8 100644 --- a/packages/amplify-provider-awscloudformation/src/index.ts +++ b/packages/amplify-provider-awscloudformation/src/index.ts @@ -54,8 +54,8 @@ function onInitSuccessful(context) { return initializer.onInitSuccessful(context); } -function pushResources(context, resourceList, rebuild: boolean = false) { - return resourcePusher.run(context, resourceList, rebuild); +function pushResources(context, resourceList) { + return resourcePusher.run(context, resourceList); } function storeCurrentCloudBackend(context) { diff --git a/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts b/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts index 04d2b94df8d..32507727607 100644 --- a/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts @@ -327,15 +327,9 @@ export class DeploymentManager { try { const response = await this.ddbClient.describeTable({ TableName: tableName }).promise(); - if (response.Table?.TableStatus === 'DELETING') { - return false; - } const gsis = response.Table?.GlobalSecondaryIndexes; return gsis ? gsis.every(idx => idx.IndexStatus === 'ACTIVE') : true; } catch (err) { - if (err?.code === 'ResourceNotFoundException') { - return true; // in the case of an iterative update that recreates a table, non-existance means the table has been fully removed - } this.logger('getTableStatus', [{ tableName }])(err); throw err; } diff --git a/packages/amplify-provider-awscloudformation/src/push-resources.ts b/packages/amplify-provider-awscloudformation/src/push-resources.ts index e738daccc94..bdf3d5ad29f 100644 --- a/packages/amplify-provider-awscloudformation/src/push-resources.ts +++ b/packages/amplify-provider-awscloudformation/src/push-resources.ts @@ -47,10 +47,6 @@ import { preProcessCFNTemplate } from './pre-push-cfn-processor/cfn-pre-processo import { AUTH_TRIGGER_STACK, AUTH_TRIGGER_TEMPLATE } from './utils/upload-auth-trigger-template'; import { ensureValidFunctionModelDependencies } from './utils/remove-dependent-function'; import { legacyLayerMigration, postPushLambdaLayerCleanup, prePushLambdaLayerPrompt } from './lambdaLayerInvocations'; -import { - postDeploymentCleanup, - prependDeploymentStepsToDisconnectFunctionsFromReplacedModelTables, -} from './disconnect-dependent-resources'; const logger = fileLogger('push-resources'); @@ -71,7 +67,7 @@ const deploymentInProgressErrorMessage = (context: $TSContext) => { context.print.error('"amplify push --force" to re-deploy'); }; -export async function run(context: $TSContext, resourceDefinition: $TSObject, rebuild: boolean = false) { +export async function run(context: $TSContext, resourceDefinition: $TSObject) { const deploymentStateManager = await DeploymentStateManager.createDeploymentStateManager(context); let iterativeDeploymentWasInvoked = false; let layerResources = []; @@ -84,7 +80,7 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re parameters: { options }, } = context; - let resources = !!context?.exeInfo?.forcePush || rebuild ? allResources : resourcesToBeCreated.concat(resourcesToBeUpdated); + let resources = !!context?.exeInfo?.forcePush ? allResources : resourcesToBeCreated.concat(resourcesToBeUpdated); layerResources = resources.filter(r => r.service === FunctionServiceNameLambdaLayer); if (deploymentStateManager.isDeploymentInProgress() && !deploymentStateManager.isDeploymentFinished()) { @@ -113,7 +109,7 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re } validateCfnTemplates(context, resources); - for (const resource of resources) { + for await (const resource of resources) { if (resource.service === ApiServiceNameElasticContainer && resource.category === 'api') { const { exposedContainer, @@ -140,7 +136,9 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re } } - for (const resource of resources.filter(r => r.category === FunctionCategoryName && r.service === FunctionServiceNameLambdaLayer)) { + for await (const resource of resources.filter( + r => r.category === FunctionCategoryName && r.service === FunctionServiceNameLambdaLayer, + )) { await legacyLayerMigration(context, resource.resourceName); } @@ -150,6 +148,7 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re await transformGraphQLSchema(context, { handleMigration: opts => updateStackForAPIMigration(context, 'api', undefined, opts), minify: options['minify'], + promptApiKeyCreation: true, }); // If there is a deployment already in progress we have to fail the push operation as another @@ -160,26 +159,17 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re } let deploymentSteps: DeploymentStep[] = []; - let functionsDependentOnReplacedModelTables: string[] = []; // location where the intermediate deployment steps are stored let stateFolder: { local?: string; cloud?: string } = {}; // Check if iterative updates are enabled or not and generate the required deployment steps if needed. if (FeatureFlags.getBoolean('graphQLTransformer.enableIterativeGSIUpdates')) { - const gqlResource = getGqlUpdatedResource(rebuild ? resources : resourcesToBeUpdated); + const gqlResource = getGqlUpdatedResource(resourcesToBeUpdated); if (gqlResource) { - const gqlManager = await GraphQLResourceManager.createInstance(context, gqlResource, cloudformationMeta.StackId, rebuild); + const gqlManager = await GraphQLResourceManager.createInstance(context, gqlResource, cloudformationMeta.StackId); deploymentSteps = await gqlManager.run(); - - // If any models are being replaced, we prepend steps to the iterative deployment to remove references to the replaced table in functions that have a dependeny on the tables - const modelsBeingReplaced = gqlManager.getTablesBeingReplaced().map(meta => meta.stackName); // stackName is the same as the model name - deploymentSteps = await prependDeploymentStepsToDisconnectFunctionsFromReplacedModelTables( - context, - modelsBeingReplaced, - deploymentSteps, - ); if (deploymentSteps.length > 1) { iterativeDeploymentWasInvoked = true; @@ -214,8 +204,7 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re resourcesToBeUpdated.length > 0 || resourcesToBeDeleted.length > 0 || tagsUpdated || - context.exeInfo.forcePush || - rebuild + context.exeInfo.forcePush ) { // If there is an API change, there will be one deployment step. But when there needs an iterative update the step count is > 1 if (deploymentSteps.length > 1) { @@ -260,11 +249,10 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re context.print.error(`Could not delete state directory locally: ${err}`); } } - const s3 = await S3.getInstance(context); if (stateFolder.cloud) { + const s3 = await S3.getInstance(context); await s3.deleteDirectory(cloudformationMeta.DeploymentBucketName, stateFolder.cloud); } - postDeploymentCleanup(s3, cloudformationMeta.DeploymentBucketName); } else { // Non iterative update spinner.start(); @@ -390,7 +378,9 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re //check for auth resources and remove deployment secret for push resources - .filter(resource => resource.category === 'auth' && resource.service === 'Cognito' && resource.providerPlugin === 'awscloudformation') + .filter( + resource => resource.category === 'auth' && resource.service === 'Cognito' && resource.providerPlugin === 'awscloudformation', + ) .map(({ category, resourceName }) => context.amplify.removeDeploymentSecrets(context, category, resourceName)); await adminModelgen(context, resources); @@ -402,7 +392,10 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re if (iterativeDeploymentWasInvoked) { await deploymentStateManager.failDeployment(); } - spinner.fail('An error occurred when pushing the resources to the cloud'); + + if(!await canAutoResolveGraphQLAuthError(error.message)) { + spinner.fail('An error occurred when pushing the resources to the cloud'); + } rollbackLambdaLayers(layerResources); @@ -412,6 +405,16 @@ export async function run(context: $TSContext, resourceDefinition: $TSObject, re } } +async function canAutoResolveGraphQLAuthError(message: string) { + if (message === `@auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.` + || message === `@auth directive with 'userPools' provider found, but the project has no Cognito User Pools authentication provider configured.` + || message === `@auth directive with 'oidc' provider found, but the project has no OPENID_CONNECT authentication provider configured.` + || message === `@auth directive with 'apiKey' provider found, but the project has no API Key authentication provider configured.` + || message === `@auth directive with 'function' provider found, but the project has no Lambda authentication provider configured.`) { + return true; + } +} + export async function updateStackForAPIMigration(context: $TSContext, category: string, resourceName: string, options: $TSAny) { const { resourcesToBeCreated, resourcesToBeUpdated, resourcesToBeDeleted, allResources } = await context.amplify.getResourceStatus( category, @@ -840,7 +843,7 @@ async function formNestedStack( nestedStack.Description = 'Root Stack for AWS Amplify Console'; } } catch (err) { - console.info('App not deployed yet.'); + // if it is not an AmplifyAdmin app, do nothing } const { amplifyMeta } = projectDetails; @@ -1144,3 +1147,4 @@ function rollbackLambdaLayers(layerResources: $TSAny[]) { stateManager.setMeta(projectRoot, meta); } } + diff --git a/packages/amplify-provider-awscloudformation/src/transform-graphql-schema.ts b/packages/amplify-provider-awscloudformation/src/transform-graphql-schema.ts index 35074daf0bb..2f4467b00de 100644 --- a/packages/amplify-provider-awscloudformation/src/transform-graphql-schema.ts +++ b/packages/amplify-provider-awscloudformation/src/transform-graphql-schema.ts @@ -13,10 +13,10 @@ import { FunctionTransformer } from 'graphql-function-transformer'; import { HttpTransformer } from 'graphql-http-transformer'; import { PredictionsTransformer } from 'graphql-predictions-transformer'; import { KeyTransformer } from 'graphql-key-transformer'; -import { destructiveUpdatesFlag, ProviderName as providerName } from './constants'; +import { ProviderName as providerName } from './constants'; import { AmplifyCLIFeatureFlagAdapter } from './utils/amplify-cli-feature-flag-adapter'; import { isAmplifyAdminApp } from './utils/admin-helpers'; -import { $TSContext, JSONUtilities, stateManager } from 'amplify-cli-core'; +import { JSONUtilities, stateManager } from 'amplify-cli-core'; import { ResourceConstants } from 'graphql-transformer-common'; import { printer } from 'amplify-prompts'; @@ -61,7 +61,9 @@ export function searchablePushChecks(context, map): void { const apiCategory = teamProviderInfo[currEnv]?.categories?.api; const instanceType = apiCategory ? apiCategory[ResourceConstants.PARAMETERS.ElasticsearchInstanceType] : null; if (!instanceType || instanceType === 't2.small.elasticsearch') { - printer.warn("Your instance type for OpenSearch is t2.small, you may experience performance issues or data loss. Consider reconfiguring with the instructions here https://docs.amplify.aws/cli/graphql-transformer/searchable/") + printer.warn( + 'Your instance type for OpenSearch is t2.small, you may experience performance issues or data loss. Consider reconfiguring with the instructions here https://docs.amplify.aws/cli/graphql-transformer/searchable/', + ); } } } @@ -167,7 +169,7 @@ function getTransformerFactory(context, resourceDir, authConfig?) { const res = await isAmplifyAdminApp(appId); amplifyAdminEnabled = res.isAdminApp; } catch (err) { - console.info('App not deployed yet.'); + // if it is not an AmplifyAdmin app, do nothing } transformerList.push(new ModelAuthTransformer({ authConfig, addAwsIamAuthInOutputSchema: amplifyAdminEnabled })); @@ -307,7 +309,7 @@ async function migrateProject(context, options) { } } -export async function transformGraphQLSchema(context: $TSContext, options) { +export async function transformGraphQLSchema(context, options) { const useExperimentalPipelineTransformer = FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer'); if (useExperimentalPipelineTransformer) { return transformGraphQLSchemaV6(context, options); @@ -378,7 +380,7 @@ export async function transformGraphQLSchema(context: $TSContext, options) { if (!parameters && fs.existsSync(parametersFilePath)) { try { - parameters = JSONUtilities.readJson(parametersFilePath); + parameters = context.amplify.readJsonFile(parametersFilePath); } catch (e) { parameters = {}; } @@ -492,8 +494,7 @@ export async function transformGraphQLSchema(context: $TSContext, options) { } const ff = new AmplifyCLIFeatureFlagAdapter(); - const allowDestructiveUpdates = context?.input?.options?.[destructiveUpdatesFlag] || context.input?.options?.force; - const sanityCheckRulesList = getSanityCheckRules(isNewAppSyncAPI, ff, allowDestructiveUpdates); + const sanityCheckRulesList = getSanityCheckRules(isNewAppSyncAPI, ff); const buildConfig = { ...options, @@ -508,7 +509,8 @@ export async function transformGraphQLSchema(context: $TSContext, options) { sanityCheckRules: sanityCheckRulesList, }; const transformerOutput = await buildAPIProject(buildConfig); - context.print.success(`\nGraphQL schema compiled successfully.\n\nEdit your schema at ${schemaFilePath} or \ + + context.print.success(`GraphQL schema compiled successfully.\n\nEdit your schema at ${schemaFilePath} or \ place .graphql files in a directory at ${schemaDirPath}`); if (!options.dryRun) { diff --git a/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts b/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts index 0cd6143c3db..13c24707899 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts @@ -17,7 +17,7 @@ export function doAdminTokensExist(appId: string): boolean { return !!stateManager.getAmplifyAdminConfigEntry(appId); } -export async function isAmplifyAdminApp(appId: string): Promise<{ isAdminApp: boolean; region: string }> { +export async function isAmplifyAdminApp(appId: string): Promise<{ isAdminApp: boolean; region: string; userPoolID: string }> { if (!appId) { throw `Failed to check if Admin UI is enabled: appId is undefined`; } @@ -25,7 +25,8 @@ export async function isAmplifyAdminApp(appId: string): Promise<{ isAdminApp: bo if (appState.appId && appState.region && appState.region !== 'us-east-1') { appState = await getAdminAppState(appId, appState.region); } - return { isAdminApp: !!appState.appId, region: appState.region }; + const userPoolID = appState.loginAuthConfig ? JSON.parse(appState.loginAuthConfig).aws_user_pools_id : ''; + return { isAdminApp: !!appState.appId, region: appState.region, userPoolID }; } export async function getTempCredsWithAdminTokens(context: $TSContext, appId: string): Promise { diff --git a/packages/amplify-provider-awscloudformation/src/utils/api-key-helpers.ts b/packages/amplify-provider-awscloudformation/src/utils/api-key-helpers.ts new file mode 100644 index 00000000000..dd2545d7ee1 --- /dev/null +++ b/packages/amplify-provider-awscloudformation/src/utils/api-key-helpers.ts @@ -0,0 +1,52 @@ +import { stateManager } from 'amplify-cli-core'; +import { ApiKeyConfig } from '@aws-amplify/graphql-transformer-interfaces'; + +export function getAppSyncApiConfig(): any { + const apiConfig = stateManager.getMeta()?.api; + let appSyncApi; + + Object.keys(apiConfig).forEach(k => { + if (apiConfig[k]['service'] === 'AppSync') appSyncApi = apiConfig[k]; + }); + + return appSyncApi; +} + +function getDefaultIfApiKey(): ApiKeyConfig { + const authConfig = getAppSyncApiConfig()?.output?.authConfig; + const { defaultAuthentication } = authConfig; + + if (defaultAuthentication.authenticationType === 'API_KEY') return defaultAuthentication.apiKeyConfig; +} + +function getAdditionalApiKeyConfig(): ApiKeyConfig { + const authConfig = getAppSyncApiConfig()?.output?.authConfig; + const { additionalAuthenticationProviders } = authConfig; + let apiKeyConfig; + + additionalAuthenticationProviders.forEach(authProvider => { + if (authProvider.authenticationType === 'API_KEY') apiKeyConfig = authProvider.apiKeyConfig; + }); + + return apiKeyConfig; +} + +export function getApiKeyConfig(): ApiKeyConfig { + const emptyConfig = {} as ApiKeyConfig; + return getDefaultIfApiKey() || getAdditionalApiKeyConfig() || emptyConfig; +} + +export function apiKeyIsActive(): boolean { + const today = new Date(); + const { apiKeyExpirationDate } = getApiKeyConfig() || {}; + + if (!apiKeyExpirationDate) return false; + + return new Date(apiKeyExpirationDate) > today; +} + +export function hasApiKey(): boolean { + const apiKeyConfig = getApiKeyConfig(); + + return !!apiKeyConfig && !!apiKeyConfig?.apiKeyExpirationDate; +} diff --git a/packages/amplify-provider-awscloudformation/src/utils/sandbox-mode-helpers.ts b/packages/amplify-provider-awscloudformation/src/utils/sandbox-mode-helpers.ts new file mode 100644 index 00000000000..86e01f99ae6 --- /dev/null +++ b/packages/amplify-provider-awscloudformation/src/utils/sandbox-mode-helpers.ts @@ -0,0 +1,55 @@ +import chalk from 'chalk'; +import { $TSContext } from 'amplify-cli-core'; +import { getApiKeyConfig, apiKeyIsActive, hasApiKey } from './api-key-helpers'; +import { printer } from 'amplify-prompts'; + +export async function showSandboxModePrompts(context: $TSContext): Promise { + if (!hasApiKey() || !apiKeyIsActive()) { + printer.info( + ` +⚠️ WARNING: Global Sandbox Mode has been enabled, which requires a valid API key. If +you'd like to disable, remove ${chalk.green('"type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key"')} +from your GraphQL schema and run 'amplify push' again. If you'd like to proceed with +sandbox mode disabled in '${context.amplify.getEnvInfo().envName}', do not create an API Key. +`, + 'yellow', + ); + return await context.amplify.invokePluginMethod(context, 'api', undefined, 'promptToAddApiKey', [context]); + } else { + showGlobalSandboxModeWarning(); + return; + } +} + +export function showGlobalSandboxModeWarning(): void { + const { apiKeyExpirationDate } = getApiKeyConfig(); + + if (!apiKeyExpirationDate) return; + + const expirationDate = new Date(apiKeyExpirationDate); + + printer.info( + ` +⚠️ WARNING: ${chalk.green('"type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key"')} in your GraphQL schema +allows public create, read, update, and delete access to all models via API Key. This +should only be used for testing purposes. API Key expiration date is: ${expirationDate.toLocaleDateString()} + +To configure PRODUCTION-READY authorization rules, review: https://docs.amplify.aws/cli/graphql-transformer/auth +`, + 'yellow', + ); +} + +export function getSandboxModeEnvNameFromDirectiveSet(input: any): string { + const sandboxModeDirective = input.find((el: any) => el.name.value === 'allow_public_data_access_with_api_key'); + + if (!sandboxModeDirective) return ''; + + const inField = sandboxModeDirective.arguments.find((el: any) => el.name.value === 'in'); + return inField.value.value; +} + +export function removeSandboxDirectiveFromSchema(schema): string { + const ampGlobalRegex = /(type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key\(in:)+(.*?)+(\))/g; + return schema.replace(ampGlobalRegex, ''); +} diff --git a/packages/amplify-python-function-runtime-provider/CHANGELOG.md b/packages/amplify-python-function-runtime-provider/CHANGELOG.md index bce1d6f55e5..003db829e0e 100644 --- a/packages/amplify-python-function-runtime-provider/CHANGELOG.md +++ b/packages/amplify-python-function-runtime-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.9.12-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-python-function-runtime-provider@1.9.11...amplify-python-function-runtime-provider@1.9.12-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-python-function-runtime-provider + + + + + ## [1.9.11](https://github.com/aws-amplify/amplify-cli/compare/amplify-python-function-runtime-provider@1.9.10...amplify-python-function-runtime-provider@1.9.11) (2021-09-18) **Note:** Version bump only for package amplify-python-function-runtime-provider diff --git a/packages/amplify-python-function-runtime-provider/package.json b/packages/amplify-python-function-runtime-provider/package.json index 1147467baba..f144e564028 100644 --- a/packages/amplify-python-function-runtime-provider/package.json +++ b/packages/amplify-python-function-runtime-provider/package.json @@ -1,6 +1,6 @@ { "name": "amplify-python-function-runtime-provider", - "version": "1.9.11", + "version": "1.9.12-graphql-vnext-dev-preview.0", "description": "Provides functionality related to functions in Python on AWS", "repository": { "type": "git", @@ -21,7 +21,7 @@ "clean": "rimraf lib tsconfig.tsbuildinfo" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-function-plugin-interface": "1.9.1", "archiver": "^5.3.0", "execa": "^5.1.1", diff --git a/packages/amplify-util-headless-input/CHANGELOG.md b/packages/amplify-util-headless-input/CHANGELOG.md index 86e614596d9..b3c44c0a824 100644 --- a/packages/amplify-util-headless-input/CHANGELOG.md +++ b/packages/amplify-util-headless-input/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.5-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-util-headless-input@1.5.4...amplify-util-headless-input@1.5.5-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-util-headless-input + + + + + ## [1.5.4](https://github.com/aws-amplify/amplify-cli/compare/amplify-util-headless-input@1.5.3...amplify-util-headless-input@1.5.4) (2021-09-14) **Note:** Version bump only for package amplify-util-headless-input diff --git a/packages/amplify-util-headless-input/package.json b/packages/amplify-util-headless-input/package.json index d2f8d21a463..d8e1c0c6859 100644 --- a/packages/amplify-util-headless-input/package.json +++ b/packages/amplify-util-headless-input/package.json @@ -1,6 +1,6 @@ { "name": "amplify-util-headless-input", - "version": "1.5.4", + "version": "1.5.5-graphql-vnext-dev-preview.0", "description": "Logic for validating objects against JSON-schema specs and performing version upgrades when necessary / possible", "main": "lib/index.js", "scripts": { @@ -16,7 +16,7 @@ "license": "Apache-2.0", "dependencies": { "ajv": "^6.12.2", - "amplify-headless-interface": "1.10.0" + "amplify-headless-interface": "1.11.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/json-schema": "^7.0.5", diff --git a/packages/amplify-util-import/CHANGELOG.md b/packages/amplify-util-import/CHANGELOG.md index 73800f1d0e1..076726a7de4 100644 --- a/packages/amplify-util-import/CHANGELOG.md +++ b/packages/amplify-util-import/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.13-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-util-import@1.5.12...amplify-util-import@1.5.13-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-util-import + + + + + ## [1.5.12](https://github.com/aws-amplify/amplify-cli/compare/amplify-util-import@1.5.11...amplify-util-import@1.5.12) (2021-09-18) **Note:** Version bump only for package amplify-util-import diff --git a/packages/amplify-util-import/package.json b/packages/amplify-util-import/package.json index 737f242f315..eb25fa57f1c 100644 --- a/packages/amplify-util-import/package.json +++ b/packages/amplify-util-import/package.json @@ -1,6 +1,6 @@ { "name": "amplify-util-import", - "version": "1.5.12", + "version": "1.5.13-graphql-vnext-dev-preview.0", "description": "Resource import support package", "main": "lib/index.js", "scripts": { @@ -16,7 +16,7 @@ "author": "Amazon Web Services", "license": "Apache-2.0", "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "aws-sdk": "^2.963.0" }, "devDependencies": { diff --git a/packages/amplify-util-mock/CHANGELOG.md b/packages/amplify-util-mock/CHANGELOG.md index 93ce3fd6244..e92891e15f7 100644 --- a/packages/amplify-util-mock/CHANGELOG.md +++ b/packages/amplify-util-mock/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.34.6-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/amplify-util-mock@3.34.5...amplify-util-mock@3.34.6-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package amplify-util-mock + + + + + ## [3.34.5](https://github.com/aws-amplify/amplify-cli/compare/amplify-util-mock@3.34.4...amplify-util-mock@3.34.5) (2021-09-18) **Note:** Version bump only for package amplify-util-mock diff --git a/packages/amplify-util-mock/package.json b/packages/amplify-util-mock/package.json index 6baa05f29d1..65c8b00a2b1 100644 --- a/packages/amplify-util-mock/package.json +++ b/packages/amplify-util-mock/package.json @@ -1,6 +1,6 @@ { "name": "amplify-util-mock", - "version": "3.34.5", + "version": "3.34.6-graphql-vnext-dev-preview.0", "description": "amplify cli plugin providing local testing", "repository": { "type": "git", @@ -26,11 +26,11 @@ "dependencies": { "@hapi/topo": "^5.0.0", "amplify-appsync-simulator": "1.27.7", - "amplify-category-function": "2.34.7", - "amplify-cli-core": "1.29.0", + "amplify-category-function": "2.35.0-graphql-vnext-dev-preview.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "amplify-codegen": "^2.23.1", - "amplify-dynamodb-simulator": "1.19.12", - "amplify-provider-awscloudformation": "4.60.1", + "amplify-dynamodb-simulator": "1.19.13-graphql-vnext-dev-preview.0", + "amplify-provider-awscloudformation": "4.61.0-graphql-vnext-dev-preview.0", "amplify-storage-simulator": "1.6.1", "chokidar": "^3.3.1", "detect-port": "^1.3.0", @@ -48,19 +48,19 @@ "@types/semver": "^7.1.0", "@types/which": "^1.3.2", "amplify-function-plugin-interface": "1.9.1", - "amplify-nodejs-function-runtime-provider": "1.6.11", + "amplify-nodejs-function-runtime-provider": "1.6.12-graphql-vnext-dev-preview.0", "aws-appsync": "^2.0.2", "aws-sdk": "^2.963.0", "aws-sdk-mock": "^5.1.0", "axios": "^0.21.4", - "graphql-auth-transformer": "6.24.22", - "graphql-connection-transformer": "4.21.22", - "graphql-dynamodb-transformer": "6.22.22", - "graphql-function-transformer": "2.5.21", - "graphql-key-transformer": "2.23.22", + "graphql-auth-transformer": "6.25.0-graphql-vnext-dev-preview.0", + "graphql-connection-transformer": "4.21.23-graphql-vnext-dev-preview.0", + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0", + "graphql-function-transformer": "2.5.22-graphql-vnext-dev-preview.0", + "graphql-key-transformer": "2.23.23-graphql-vnext-dev-preview.0", "graphql-tag": "^2.10.1", - "graphql-transformer-core": "6.29.7", - "graphql-versioned-transformer": "4.17.22", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", + "graphql-versioned-transformer": "4.17.23-graphql-vnext-dev-preview.0", "isomorphic-fetch": "^3.0.0", "jsonwebtoken": "^8.5.1", "uuid": "^3.4.0", diff --git a/packages/graphql-auth-transformer/CHANGELOG.md b/packages/graphql-auth-transformer/CHANGELOG.md index 2c1ae590937..a86461f2771 100644 --- a/packages/graphql-auth-transformer/CHANGELOG.md +++ b/packages/graphql-auth-transformer/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [6.25.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-auth-transformer@6.24.22...graphql-auth-transformer@6.25.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **graphql-model-transformer:** [@model](https://github.com/model) conflict resolution ([#8035](https://github.com/aws-amplify/amplify-cli/issues/8035)) ([f3bdc4a](https://github.com/aws-amplify/amplify-cli/commit/f3bdc4ac1fcf596f634d9d2e968785e76f7b138c)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + ## [6.24.22](https://github.com/aws-amplify/amplify-cli/compare/graphql-auth-transformer@6.24.21...graphql-auth-transformer@6.24.22) (2021-09-18) **Note:** Version bump only for package graphql-auth-transformer diff --git a/packages/graphql-auth-transformer/package.json b/packages/graphql-auth-transformer/package.json index 5e647d20d3e..f7e4e99575a 100644 --- a/packages/graphql-auth-transformer/package.json +++ b/packages/graphql-auth-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-auth-transformer", - "version": "6.24.22", + "version": "6.25.0-graphql-vnext-dev-preview.0", "description": "Implements the @auth directive for the appsync model transform.", "repository": { "type": "git", @@ -24,17 +24,17 @@ }, "dependencies": { "graphql": "^14.5.8", - "graphql-connection-transformer": "4.21.22", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-connection-transformer": "4.21.23-graphql-vnext-dev-preview.0", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/node": "^12.12.6", "cloudform-types": "^4.2.0", - "graphql-dynamodb-transformer": "6.22.22", - "graphql-elasticsearch-transformer": "4.12.1", - "graphql-function-transformer": "2.5.21", + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0", + "graphql-elasticsearch-transformer": "4.12.2-graphql-vnext-dev-preview.0", + "graphql-function-transformer": "2.5.22-graphql-vnext-dev-preview.0", "rimraf": "^3.0.0" }, "jest": { diff --git a/packages/graphql-auth-transformer/src/AuthRule.ts b/packages/graphql-auth-transformer/src/AuthRule.ts index 2ea6854ed0a..17563baa644 100644 --- a/packages/graphql-auth-transformer/src/AuthRule.ts +++ b/packages/graphql-auth-transformer/src/AuthRule.ts @@ -1,5 +1,5 @@ -export type AuthStrategy = 'owner' | 'groups' | 'public' | 'private'; -export type AuthProvider = 'apiKey' | 'iam' | 'oidc' | 'userPools'; +export type AuthStrategy = 'owner' | 'groups' | 'public' | 'private' | 'custom'; +export type AuthProvider = 'apiKey' | 'iam' | 'oidc' | 'userPools' | 'function'; export type ModelQuery = 'get' | 'list'; export type ModelMutation = 'create' | 'update' | 'delete'; export type ModelOperation = 'create' | 'update' | 'delete' | 'read'; diff --git a/packages/graphql-auth-transformer/src/ModelAuthTransformer.ts b/packages/graphql-auth-transformer/src/ModelAuthTransformer.ts index cf2fab44ac3..1d8ed98c631 100644 --- a/packages/graphql-auth-transformer/src/ModelAuthTransformer.ts +++ b/packages/graphql-auth-transformer/src/ModelAuthTransformer.ts @@ -84,7 +84,7 @@ import { OWNER_AUTH_STRATEGY, GROUPS_AUTH_STRATEGY, DEFAULT_OWNER_FIELD, AUTH_NO * attributes of the records using conditional expressions. This will likely * be via a new argument such as "groupsField". */ -export type AppSyncAuthMode = 'API_KEY' | 'AMAZON_COGNITO_USER_POOLS' | 'AWS_IAM' | 'OPENID_CONNECT'; +export type AppSyncAuthMode = 'API_KEY' | 'AMAZON_COGNITO_USER_POOLS' | 'AWS_IAM' | 'OPENID_CONNECT' | 'AWS_LAMBDA'; export type AppSyncAuthConfiguration = { defaultAuthentication: AppSyncAuthConfigurationEntry; additionalAuthenticationProviders: Array; @@ -94,10 +94,12 @@ export type AppSyncAuthConfigurationEntry = { apiKeyConfig?: ApiKeyConfig; userPoolConfig?: UserPoolConfig; openIDConnectConfig?: OpenIDConnectConfig; + lambdaAuthorizerConfig?: LambdaAuthorizerConfig; }; export type ApiKeyConfig = { description?: string; apiKeyExpirationDays: number; + apiKeyExpirationDate?: Date; }; export type UserPoolConfig = { userPoolId: string; @@ -109,6 +111,10 @@ export type OpenIDConnectConfig = { iatTTL?: number; authTTL?: number; }; +export type LambdaAuthorizerConfig = { + lambdaFunction: string; + ttlSeconds?: number; +}; const validateAuthModes = (authConfig: AppSyncAuthConfiguration) => { let additionalAuthModes = []; @@ -122,7 +128,7 @@ const validateAuthModes = (authConfig: AppSyncAuthConfiguration) => { for (let i = 0; i < authModes.length; i++) { const mode = authModes[i]; - if (mode !== 'API_KEY' && mode !== 'AMAZON_COGNITO_USER_POOLS' && mode !== 'AWS_IAM' && mode !== 'OPENID_CONNECT') { + if (mode !== 'API_KEY' && mode !== 'AMAZON_COGNITO_USER_POOLS' && mode !== 'AWS_IAM' && mode !== 'OPENID_CONNECT' && mode !== 'AWS_LAMBDA') { throw new Error(`Invalid auth mode ${mode}`); } } @@ -1387,9 +1393,8 @@ operations will be generated by the CLI.`, // In create mutations, the dynamic group and ownership authorization checks // are done before calling PutItem. - const dynamicGroupAuthorizationExpression = this.resources.dynamicGroupAuthorizationExpressionForCreateOperations( - dynamicGroupAuthorizationRules, - ); + const dynamicGroupAuthorizationExpression = + this.resources.dynamicGroupAuthorizationExpressionForCreateOperations(dynamicGroupAuthorizationRules); const fieldIsList = (fieldName: string) => { const field = parent.fields.find(field => field.name.value === fieldName); if (field) { @@ -1547,9 +1552,8 @@ operations will be generated by the CLI.`, ]), ); - const throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty = this.resources.throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty( - field, - ); + const throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty = + this.resources.throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty(field); // If we've any modes to check, then add the authMode check code block // to the start of the resolver. diff --git a/packages/graphql-connection-transformer/CHANGELOG.md b/packages/graphql-connection-transformer/CHANGELOG.md index 3cd9eec7d28..937c30ae199 100644 --- a/packages/graphql-connection-transformer/CHANGELOG.md +++ b/packages/graphql-connection-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.21.23-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-connection-transformer@4.21.22...graphql-connection-transformer@4.21.23-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-connection-transformer + + + + + ## [4.21.22](https://github.com/aws-amplify/amplify-cli/compare/graphql-connection-transformer@4.21.21...graphql-connection-transformer@4.21.22) (2021-09-18) **Note:** Version bump only for package graphql-connection-transformer diff --git a/packages/graphql-connection-transformer/package.json b/packages/graphql-connection-transformer/package.json index 04d5165605f..71af6c1f1e7 100644 --- a/packages/graphql-connection-transformer/package.json +++ b/packages/graphql-connection-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-connection-transformer", - "version": "4.21.22", + "version": "4.21.23-graphql-vnext-dev-preview.0", "description": "An AppSync model transform for connecting objects.", "repository": { "type": "git", @@ -24,11 +24,11 @@ "dependencies": { "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-dynamodb-transformer": "6.22.22", - "graphql-key-transformer": "2.23.22", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0", + "graphql-key-transformer": "2.23.23-graphql-vnext-dev-preview.0", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/node": "^12.12.6" diff --git a/packages/graphql-dynamodb-transformer/CHANGELOG.md b/packages/graphql-dynamodb-transformer/CHANGELOG.md index 2c72c94f34b..c851881223f 100644 --- a/packages/graphql-dynamodb-transformer/CHANGELOG.md +++ b/packages/graphql-dynamodb-transformer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [6.22.23-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-dynamodb-transformer@6.22.22...graphql-dynamodb-transformer@6.22.23-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **graphql-model-transformer:** iam role name does not exceed 64 characters ([#8244](https://github.com/aws-amplify/amplify-cli/issues/8244)) ([812a671](https://github.com/aws-amplify/amplify-cli/commit/812a67163d6dd33160bf7ace9afd538c83a7af1a)) + + + + + ## [6.22.22](https://github.com/aws-amplify/amplify-cli/compare/graphql-dynamodb-transformer@6.22.21...graphql-dynamodb-transformer@6.22.22) (2021-09-18) **Note:** Version bump only for package graphql-dynamodb-transformer diff --git a/packages/graphql-dynamodb-transformer/package.json b/packages/graphql-dynamodb-transformer/package.json index 3c1517b6d42..0b77e54e35d 100644 --- a/packages/graphql-dynamodb-transformer/package.json +++ b/packages/graphql-dynamodb-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-dynamodb-transformer", - "version": "6.22.22", + "version": "6.22.23-graphql-vnext-dev-preview.0", "description": "An AppSync model transform that takes a simple model and creates a DynamoDB table, DynamoDB stream.", "repository": { "type": "git", @@ -26,9 +26,9 @@ "@types/pluralize": "^0.0.29", "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "md5": "^2.2.1", "pluralize": "^8.0.0" }, diff --git a/packages/graphql-elasticsearch-transformer/CHANGELOG.md b/packages/graphql-elasticsearch-transformer/CHANGELOG.md index 46fd201e21b..eee78ad8513 100644 --- a/packages/graphql-elasticsearch-transformer/CHANGELOG.md +++ b/packages/graphql-elasticsearch-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.12.2-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-elasticsearch-transformer@4.12.1...graphql-elasticsearch-transformer@4.12.2-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-elasticsearch-transformer + + + + + ## [4.12.1](https://github.com/aws-amplify/amplify-cli/compare/graphql-elasticsearch-transformer@4.12.0...graphql-elasticsearch-transformer@4.12.1) (2021-09-18) **Note:** Version bump only for package graphql-elasticsearch-transformer diff --git a/packages/graphql-elasticsearch-transformer/package.json b/packages/graphql-elasticsearch-transformer/package.json index e7c11a60217..f3176907da9 100644 --- a/packages/graphql-elasticsearch-transformer/package.json +++ b/packages/graphql-elasticsearch-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-elasticsearch-transformer", - "version": "4.12.1", + "version": "4.12.2-graphql-vnext-dev-preview.0", "description": "An AppSync model transform that creates an ElasticSearch index with the queries to match.", "repository": { "type": "git", @@ -25,14 +25,14 @@ "dependencies": { "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/node": "^12.12.6", "bestzip": "^2.1.5", - "graphql-dynamodb-transformer": "6.22.22" + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0" }, "jest": { "transform": { diff --git a/packages/graphql-function-transformer/CHANGELOG.md b/packages/graphql-function-transformer/CHANGELOG.md index 02085da4117..fde3d370f8e 100644 --- a/packages/graphql-function-transformer/CHANGELOG.md +++ b/packages/graphql-function-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.5.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-function-transformer@2.5.21...graphql-function-transformer@2.5.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-function-transformer + + + + + ## [2.5.21](https://github.com/aws-amplify/amplify-cli/compare/graphql-function-transformer@2.5.20...graphql-function-transformer@2.5.21) (2021-09-18) **Note:** Version bump only for package graphql-function-transformer diff --git a/packages/graphql-function-transformer/package.json b/packages/graphql-function-transformer/package.json index a3db15794d8..20b398f2123 100644 --- a/packages/graphql-function-transformer/package.json +++ b/packages/graphql-function-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-function-transformer", - "version": "2.5.21", + "version": "2.5.22-graphql-vnext-dev-preview.0", "description": "Implements the @function directive.", "repository": { "type": "git", @@ -25,9 +25,9 @@ "dependencies": { "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "jest": { "transform": { diff --git a/packages/graphql-http-transformer/CHANGELOG.md b/packages/graphql-http-transformer/CHANGELOG.md index 7d8e78f89cf..84568c00884 100644 --- a/packages/graphql-http-transformer/CHANGELOG.md +++ b/packages/graphql-http-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.18.10-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-http-transformer@4.18.9...graphql-http-transformer@4.18.10-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-http-transformer + + + + + ## [4.18.9](https://github.com/aws-amplify/amplify-cli/compare/graphql-http-transformer@4.18.8...graphql-http-transformer@4.18.9) (2021-09-18) **Note:** Version bump only for package graphql-http-transformer diff --git a/packages/graphql-http-transformer/package.json b/packages/graphql-http-transformer/package.json index eebea889985..cfacea0fcdc 100644 --- a/packages/graphql-http-transformer/package.json +++ b/packages/graphql-http-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-http-transformer", - "version": "4.18.9", + "version": "4.18.10-graphql-vnext-dev-preview.0", "description": "An AppSync model transform for HTTP resolvers.", "repository": { "type": "git", @@ -24,9 +24,9 @@ "dependencies": { "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/node": "^12.12.6" diff --git a/packages/graphql-key-transformer/CHANGELOG.md b/packages/graphql-key-transformer/CHANGELOG.md index e118927d5a6..8d0f6af4d67 100644 --- a/packages/graphql-key-transformer/CHANGELOG.md +++ b/packages/graphql-key-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.23.23-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-key-transformer@2.23.22...graphql-key-transformer@2.23.23-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-key-transformer + + + + + ## [2.23.22](https://github.com/aws-amplify/amplify-cli/compare/graphql-key-transformer@2.23.21...graphql-key-transformer@2.23.22) (2021-09-18) **Note:** Version bump only for package graphql-key-transformer diff --git a/packages/graphql-key-transformer/package.json b/packages/graphql-key-transformer/package.json index 7c0548ba660..47b92396726 100644 --- a/packages/graphql-key-transformer/package.json +++ b/packages/graphql-key-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-key-transformer", - "version": "2.23.22", + "version": "2.23.23-graphql-vnext-dev-preview.0", "description": "Implements the @key directive.", "repository": { "type": "git", @@ -24,10 +24,10 @@ "dependencies": { "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-dynamodb-transformer": "6.22.22", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "jest": { "testURL": "http://localhost", diff --git a/packages/graphql-mapping-template/CHANGELOG.md b/packages/graphql-mapping-template/CHANGELOG.md index 7880448aae6..2324d80e1b9 100644 --- a/packages/graphql-mapping-template/CHANGELOG.md +++ b/packages/graphql-mapping-template/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.19.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-mapping-template@4.18.3...graphql-mapping-template@4.19.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) + + + + + ## [4.18.3](https://github.com/aws-amplify/amplify-cli/compare/graphql-mapping-template@4.18.2...graphql-mapping-template@4.18.3) (2021-08-24) **Note:** Version bump only for package graphql-mapping-template diff --git a/packages/graphql-mapping-template/package.json b/packages/graphql-mapping-template/package.json index 2e97102c65c..536d5ea08f4 100644 --- a/packages/graphql-mapping-template/package.json +++ b/packages/graphql-mapping-template/package.json @@ -1,6 +1,6 @@ { "name": "graphql-mapping-template", - "version": "4.18.3", + "version": "4.19.0-graphql-vnext-dev-preview.0", "description": "An AST wrapper around AWS AppSync resolvers", "repository": { "type": "git", diff --git a/packages/graphql-mapping-template/src/print.ts b/packages/graphql-mapping-template/src/print.ts index bd5a90f853c..b871264e156 100644 --- a/packages/graphql-mapping-template/src/print.ts +++ b/packages/graphql-mapping-template/src/print.ts @@ -115,7 +115,7 @@ function printReference(node: ReferenceNode): string { } function printQuietReference(node: QuietReferenceNode, indent: string = ''): string { - const val = typeof node.value === 'string' ? node.value : printExpr(node.value) + const val = typeof node.value === 'string' ? node.value : printExpr(node.value); return `${indent}$util.qr(${val})`; } diff --git a/packages/graphql-predictions-transformer/CHANGELOG.md b/packages/graphql-predictions-transformer/CHANGELOG.md index 13da6401382..cde15537a6c 100644 --- a/packages/graphql-predictions-transformer/CHANGELOG.md +++ b/packages/graphql-predictions-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.5.22-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-predictions-transformer@2.5.21...graphql-predictions-transformer@2.5.22-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-predictions-transformer + + + + + ## [2.5.21](https://github.com/aws-amplify/amplify-cli/compare/graphql-predictions-transformer@2.5.20...graphql-predictions-transformer@2.5.21) (2021-09-18) **Note:** Version bump only for package graphql-predictions-transformer diff --git a/packages/graphql-predictions-transformer/package.json b/packages/graphql-predictions-transformer/package.json index b18c50c4150..7b3c82f1f34 100644 --- a/packages/graphql-predictions-transformer/package.json +++ b/packages/graphql-predictions-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-predictions-transformer", - "version": "2.5.21", + "version": "2.5.22-graphql-vnext-dev-preview.0", "description": "Implements the @predictions directive.", "repository": { "type": "git", @@ -25,9 +25,9 @@ "dependencies": { "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "bestzip": "^2.1.5" diff --git a/packages/graphql-relational-schema-transformer/CHANGELOG.md b/packages/graphql-relational-schema-transformer/CHANGELOG.md index fcea8b3664b..553c68d90e9 100644 --- a/packages/graphql-relational-schema-transformer/CHANGELOG.md +++ b/packages/graphql-relational-schema-transformer/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.18.7-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-relational-schema-transformer@2.18.6...graphql-relational-schema-transformer@2.18.7-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* [#8223](https://github.com/aws-amplify/amplify-cli/issues/8223), conversion to typescript ([#8245](https://github.com/aws-amplify/amplify-cli/issues/8245)) ([096e6ca](https://github.com/aws-amplify/amplify-cli/commit/096e6ca19b94aa40ef249ea98d008380395afa16)) +* **graphql-relational-transformer:** fixes broken list and update resolvers ([#8101](https://github.com/aws-amplify/amplify-cli/issues/8101)) ([e61b362](https://github.com/aws-amplify/amplify-cli/commit/e61b362b75e6dfce6406e35e5ab52ffbaf718483)), closes [#8008](https://github.com/aws-amplify/amplify-cli/issues/8008) + + + + + ## [2.18.6](https://github.com/aws-amplify/amplify-cli/compare/graphql-relational-schema-transformer@2.18.5...graphql-relational-schema-transformer@2.18.6) (2021-09-02) **Note:** Version bump only for package graphql-relational-schema-transformer diff --git a/packages/graphql-relational-schema-transformer/package.json b/packages/graphql-relational-schema-transformer/package.json index 0fad9c15167..f0027fb79ec 100644 --- a/packages/graphql-relational-schema-transformer/package.json +++ b/packages/graphql-relational-schema-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-relational-schema-transformer", - "version": "2.18.6", + "version": "2.18.7-graphql-vnext-dev-preview.0", "description": "An AppSync model transform that takes a relational database and turns that into a GraphQL API.", "repository": { "type": "git", @@ -27,8 +27,8 @@ "cloudform-types": "^4.2.0", "fs-extra": "^8.1.0", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/fs-extra": "^8.0.1", diff --git a/packages/graphql-transformer-common/CHANGELOG.md b/packages/graphql-transformer-common/CHANGELOG.md index 6f34c8f8f2f..d2c142ef8de 100644 --- a/packages/graphql-transformer-common/CHANGELOG.md +++ b/packages/graphql-transformer-common/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.19.10-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-transformer-common@4.19.9...graphql-transformer-common@4.19.10-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **graphql-model-transformer:** iam role name does not exceed 64 characters ([#8244](https://github.com/aws-amplify/amplify-cli/issues/8244)) ([812a671](https://github.com/aws-amplify/amplify-cli/commit/812a67163d6dd33160bf7ace9afd538c83a7af1a)) + + + + + ## [4.19.9](https://github.com/aws-amplify/amplify-cli/compare/graphql-transformer-common@4.19.8...graphql-transformer-common@4.19.9) (2021-09-02) **Note:** Version bump only for package graphql-transformer-common diff --git a/packages/graphql-transformer-common/package.json b/packages/graphql-transformer-common/package.json index 56c8e27db91..138d6f53960 100644 --- a/packages/graphql-transformer-common/package.json +++ b/packages/graphql-transformer-common/package.json @@ -1,6 +1,6 @@ { "name": "graphql-transformer-common", - "version": "4.19.9", + "version": "4.19.10-graphql-vnext-dev-preview.0", "description": "Common code and constants for AppSync Transformers", "repository": { "type": "git", @@ -24,7 +24,7 @@ }, "dependencies": { "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", "md5": "^2.2.1", "pluralize": "8.0.0" }, diff --git a/packages/graphql-transformer-core/CHANGELOG.md b/packages/graphql-transformer-core/CHANGELOG.md index 7cc1f30cc14..3acefdbe5cc 100644 --- a/packages/graphql-transformer-core/CHANGELOG.md +++ b/packages/graphql-transformer-core/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [6.30.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-transformer-core@6.29.7...graphql-transformer-core@6.30.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Features + +* **cli-api:** improve add and update api ([02af6b5](https://github.com/aws-amplify/amplify-cli/commit/02af6b582367b584ad755c889fb04722af355368)) +* Flag to allow schema changes that require table replacement ([#8144](https://github.com/aws-amplify/amplify-cli/issues/8144)) ([2d4e65a](https://github.com/aws-amplify/amplify-cli/commit/2d4e65acfd034d33c6fa8ac1f5f8582e7e3bc399)) + + +### Reverts + +* Revert "feat: Flag to allow schema changes that require table replacement (#8144)" (#8268) ([bb67a35](https://github.com/aws-amplify/amplify-cli/commit/bb67a35fc81913264d9a29088b0eb37b8d13a666)), closes [#8144](https://github.com/aws-amplify/amplify-cli/issues/8144) [#8268](https://github.com/aws-amplify/amplify-cli/issues/8268) + + + + + ## [6.29.7](https://github.com/aws-amplify/amplify-cli/compare/graphql-transformer-core@6.29.6...graphql-transformer-core@6.29.7) (2021-09-18) **Note:** Version bump only for package graphql-transformer-core diff --git a/packages/graphql-transformer-core/package.json b/packages/graphql-transformer-core/package.json index c057c6bcda9..1643c94175f 100644 --- a/packages/graphql-transformer-core/package.json +++ b/packages/graphql-transformer-core/package.json @@ -1,6 +1,6 @@ { "name": "graphql-transformer-core", - "version": "6.29.7", + "version": "6.30.0-graphql-vnext-dev-preview.0", "description": "A framework to transform from GraphQL SDL to AWS CloudFormation.", "repository": { "type": "git", @@ -23,13 +23,13 @@ "clean": "rimraf ./lib" }, "dependencies": { - "amplify-cli-core": "1.29.0", + "amplify-cli-core": "1.30.0-graphql-vnext-dev-preview.0", "cloudform-types": "^4.2.0", "deep-diff": "^1.0.2", "fs-extra": "^8.1.0", "glob": "^7.1.6", "graphql": "^14.5.8", - "graphql-transformer-common": "4.19.9" + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/fs-extra": "^8.0.1", diff --git a/packages/graphql-transformer-core/src/__tests__/util/__snapshots__/amplifyUtils.test.ts.snap b/packages/graphql-transformer-core/src/__tests__/util/__snapshots__/amplifyUtils.test.ts.snap index 3ffd983d87e..e88c9b1aabf 100644 --- a/packages/graphql-transformer-core/src/__tests__/util/__snapshots__/amplifyUtils.test.ts.snap +++ b/packages/graphql-transformer-core/src/__tests__/util/__snapshots__/amplifyUtils.test.ts.snap @@ -1,38 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`get sanity check rules sanity check rule list when destructive changes flag is present and ff enabled 1`] = `Array []`; - -exports[`get sanity check rules sanity check rule list when destructive changes flag is present and ff enabled 2`] = ` -Array [ - "cantHaveMoreThan500ResourcesRule", -] -`; - -exports[`get sanity check rules sanity check rule list when destructive changes flag is present but ff not enabled 1`] = ` -Array [ - "cantEditKeySchemaRule", - "cantAddLSILaterRule", - "cantRemoveLSILater", - "cantEditLSIKeySchemaRule", - "cantEditGSIKeySchemaRule", - "cantAddAndRemoveGSIAtSameTimeRule", -] -`; - -exports[`get sanity check rules sanity check rule list when destructive changes flag is present but ff not enabled 2`] = ` -Array [ - "cantHaveMoreThan500ResourcesRule", - "cantMutateMultipleGSIAtUpdateTimeRule", -] -`; - exports[`get sanity check rules sanitycheck rule list when api is in update status and ff enabled 1`] = ` Array [ "cantEditKeySchemaRule", "cantAddLSILaterRule", "cantRemoveLSILater", "cantEditLSIKeySchemaRule", - "cantRemoveTableAfterCreation", ] `; @@ -50,7 +23,6 @@ Array [ "cantEditLSIKeySchemaRule", "cantEditGSIKeySchemaRule", "cantAddAndRemoveGSIAtSameTimeRule", - "cantRemoveTableAfterCreation", ] `; diff --git a/packages/graphql-transformer-core/src/__tests__/util/amplifyUtils.test.ts b/packages/graphql-transformer-core/src/__tests__/util/amplifyUtils.test.ts index 40f681ea908..7d4034acbc3 100644 --- a/packages/graphql-transformer-core/src/__tests__/util/amplifyUtils.test.ts +++ b/packages/graphql-transformer-core/src/__tests__/util/amplifyUtils.test.ts @@ -32,24 +32,4 @@ describe('get sanity check rules', () => { expect(diffRulesFn).toMatchSnapshot(); expect(projectRulesFn).toMatchSnapshot(); }); - - test('sanity check rule list when destructive changes flag is present and ff enabled', () => { - const ff_mock = new AmplifyCLIFeatureFlagAdapter(); - (FeatureFlags.getBoolean).mockReturnValue(true); - const sanityCheckRules: SanityCheckRules = getSanityCheckRules(false, ff_mock, true); - const diffRulesFn = sanityCheckRules.diffRules.map(func => func.name); - const projectRulesFn = sanityCheckRules.projectRules.map(func => func.name); - expect(diffRulesFn).toMatchSnapshot(); - expect(projectRulesFn).toMatchSnapshot(); - }); - - test('sanity check rule list when destructive changes flag is present but ff not enabled', () => { - const ff_mock = new AmplifyCLIFeatureFlagAdapter(); - (FeatureFlags.getBoolean).mockReturnValue(false); - const sanityCheckRules: SanityCheckRules = getSanityCheckRules(false, ff_mock, true); - const diffRulesFn = sanityCheckRules.diffRules.map(func => func.name); - const projectRulesFn = sanityCheckRules.projectRules.map(func => func.name); - expect(diffRulesFn).toMatchSnapshot(); - expect(projectRulesFn).toMatchSnapshot(); - }); }); diff --git a/packages/graphql-transformer-core/src/collectDirectives.ts b/packages/graphql-transformer-core/src/collectDirectives.ts index ee8ae2a16a4..4cd2c103b38 100644 --- a/packages/graphql-transformer-core/src/collectDirectives.ts +++ b/packages/graphql-transformer-core/src/collectDirectives.ts @@ -62,6 +62,9 @@ export function collectDirectivesByTypeNames(sdl: string) { } export function collectDirectivesByType(sdl: string): Object { + if (!sdl) { + return {}; + } const doc = parse(sdl); // defined types with directives list let types = {}; diff --git a/packages/graphql-transformer-core/src/errors.ts b/packages/graphql-transformer-core/src/errors.ts index 78114f1a7b7..dbfb14b1bb4 100644 --- a/packages/graphql-transformer-core/src/errors.ts +++ b/packages/graphql-transformer-core/src/errors.ts @@ -41,37 +41,23 @@ export class TransformerContractError extends Error { } } -export class DestructiveMigrationError extends Error { - constructor(message: string, private removedModels: string[], private replacedModels: string[]) { - super(message); - Object.setPrototypeOf(this, new.target.prototype); - this.name = 'DestructiveMigrationError'; - const prependSpace = (str: string) => ` ${str}`; - const removedModelsList = this.removedModels.map(prependSpace).toString().trim(); - const replacedModelsList = this.replacedModels.map(prependSpace).toString().trim(); - if (removedModelsList && replacedModelsList) { - this.message = `${this.message}\nThis update will remove table(s) [${removedModelsList}] and will replace table(s) [${replacedModelsList}]`; - } else if (removedModelsList) { - this.message = `${this.message}\nThis update will remove table(s) [${removedModelsList}]`; - } else if (replacedModelsList) { - this.message = `${this.message}\nThis update will replace table(s) [${replacedModelsList}]`; - } - this.message = `${this.message}\nALL EXISTING DATA IN THESE TABLES WILL BE LOST!\nIf this is intended, rerun the command with '--allow-destructuve-graphql-schema-updates'.`; - } - toString = () => this.message; -} - /** * Thrown by the sanity checker when a user is trying to make a migration that is known to not work. */ export class InvalidMigrationError extends Error { - constructor(message: string, public cause: string, public fix: string) { + fix: string; + cause: string; + constructor(message: string, cause: string, fix: string) { super(message); - Object.setPrototypeOf(this, new.target.prototype); + Object.setPrototypeOf(this, InvalidMigrationError.prototype); this.name = 'InvalidMigrationError'; + this.fix = fix; + this.cause = cause; } - toString = () => `${this.message}\nCause: ${this.cause}\nHow to fix: ${this.fix}`; } +InvalidMigrationError.prototype.toString = function () { + return `${this.message}\nCause: ${this.cause}\nHow to fix: ${this.fix}`; +}; export class InvalidGSIMigrationError extends InvalidMigrationError { fix: string; diff --git a/packages/graphql-transformer-core/src/util/amplifyUtils.ts b/packages/graphql-transformer-core/src/util/amplifyUtils.ts index 436dfb6177f..869fafc1914 100644 --- a/packages/graphql-transformer-core/src/util/amplifyUtils.ts +++ b/packages/graphql-transformer-core/src/util/amplifyUtils.ts @@ -10,17 +10,16 @@ import { writeConfig, TransformConfig, TransformMigrationConfig, loadProject, re import { FeatureFlagProvider } from '../FeatureFlags'; import { cantAddAndRemoveGSIAtSameTimeRule, - getCantAddLSILaterRule, - getCantRemoveLSILater, + cantAddLSILaterRule, + cantRemoveLSILater, cantEditGSIKeySchemaRule, - getCantEditKeySchemaRule, - getCantEditLSIKeySchemaRule, + cantEditKeySchemaRule, + cantEditLSIKeySchemaRule, cantHaveMoreThan500ResourcesRule, DiffRule, sanityCheckProject, ProjectRule, cantMutateMultipleGSIAtUpdateTimeRule, - cantRemoveTableAfterCreation, } from './sanity-check'; export const CLOUDFORMATION_FILE_NAME = 'cloudformation-template.json'; @@ -728,48 +727,34 @@ function getOrDefault(o: any, k: string, d: any) { return o[k] || d; } -export function getSanityCheckRules(isNewAppSyncAPI: boolean, ff: FeatureFlagProvider, allowDestructiveUpdates: boolean = false) { +export function getSanityCheckRules(isNewAppSyncAPI: boolean, ff: FeatureFlagProvider) { let diffRules: DiffRule[] = []; let projectRules: ProjectRule[] = []; // If we have iterative GSI upgrades enabled it means we only do sanity check on LSIs // as the other checks will be carried out as series of updates. if (!isNewAppSyncAPI) { - const iterativeUpdatesEnabled = ff.getBoolean('enableIterativeGSIUpdates'); - if (iterativeUpdatesEnabled) { - if (!allowDestructiveUpdates) { - diffRules.push( - // primary key rule - getCantEditKeySchemaRule(iterativeUpdatesEnabled), - - // LSI rules - getCantAddLSILaterRule(iterativeUpdatesEnabled), - getCantRemoveLSILater(iterativeUpdatesEnabled), - getCantEditLSIKeySchemaRule(iterativeUpdatesEnabled), - - // remove table rules - cantRemoveTableAfterCreation, - ); - } + if (ff.getBoolean('enableIterativeGSIUpdates')) { + diffRules.push( + // LSI + cantEditKeySchemaRule, + cantAddLSILaterRule, + cantRemoveLSILater, + cantEditLSIKeySchemaRule, + ); - // Project level rule + // Project level rules projectRules.push(cantHaveMoreThan500ResourcesRule); } else { diffRules.push( - // primary key rule - getCantEditKeySchemaRule(), - - // LSI rules - getCantAddLSILaterRule(), - getCantRemoveLSILater(), - getCantEditLSIKeySchemaRule(), - - // GSI rules + // LSI + cantEditKeySchemaRule, + cantAddLSILaterRule, + cantRemoveLSILater, + cantEditLSIKeySchemaRule, + // GSI cantEditGSIKeySchemaRule, cantAddAndRemoveGSIAtSameTimeRule, ); - if (!allowDestructiveUpdates) { - diffRules.push(cantRemoveTableAfterCreation); - } projectRules.push(cantHaveMoreThan500ResourcesRule, cantMutateMultipleGSIAtUpdateTimeRule); } diff --git a/packages/graphql-transformer-core/src/util/sanity-check.ts b/packages/graphql-transformer-core/src/util/sanity-check.ts index 1e2312c00ea..a043673b3ef 100644 --- a/packages/graphql-transformer-core/src/util/sanity-check.ts +++ b/packages/graphql-transformer-core/src/util/sanity-check.ts @@ -1,11 +1,11 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import _ from 'lodash'; -import { Template, ResourceBase } from 'cloudform-types'; +import { Template } from 'cloudform-types'; import { JSONUtilities } from 'amplify-cli-core'; import { diff as getDiffs, Diff as DeepDiff } from 'deep-diff'; import { readFromPath } from './fileUtils'; -import { InvalidMigrationError, InvalidGSIMigrationError, DestructiveMigrationError } from '../errors'; +import { InvalidMigrationError, InvalidGSIMigrationError } from '../errors'; import { TRANSFORM_CONFIG_FILE_NAME } from '..'; type Diff = DeepDiff; @@ -73,29 +73,18 @@ export const sanityCheckDiffs = ( * @param currentBuild The last deployed build. * @param nextBuild The next build. */ -export const getCantEditKeySchemaRule = (iterativeUpdatesEnabled: boolean = false) => { - const cantEditKeySchemaRule = (diff: Diff): void => { - if (diff.kind === 'E' && diff.path.length === 8 && diff.path[5] === 'KeySchema') { - // diff.path = [ "stacks", "Todo.json", "Resources", "TodoTable", "Properties", "KeySchema", 0, "AttributeName"] - const stackName = path.basename(diff.path[1], '.json'); - const tableName = diff.path[3]; - - if (iterativeUpdatesEnabled) { - throw new DestructiveMigrationError( - 'Editing the primary key of a model requires replacement of the underlying DynamoDB table.', - [], - [tableName], - ); - } +export const cantEditKeySchemaRule = (diff: Diff): void => { + if (diff.kind === 'E' && diff.path.length === 8 && diff.path[5] === 'KeySchema') { + // diff.path = [ "stacks", "Todo.json", "Resources", "TodoTable", "Properties", "KeySchema", 0, "AttributeName"] + const stackName = path.basename(diff.path[1], '.json'); + const tableName = diff.path[3]; - throw new InvalidMigrationError( - `Attempting to edit the key schema of the ${tableName} table in the ${stackName} stack. `, - 'Adding a primary @key directive to an existing @model. ', - 'Remove the @key directive or provide a name e.g @key(name: "ByStatus", fields: ["status"]).', - ); - } - }; - return cantEditKeySchemaRule; + throw new InvalidMigrationError( + `Attempting to edit the key schema of the ${tableName} table in the ${stackName} stack. `, + 'Adding a primary @key directive to an existing @model. ', + 'Remove the @key directive or provide a name e.g @key(name: "ByStatus", fields: ["status"]).', + ); + } }; /** @@ -105,35 +94,24 @@ export const getCantEditKeySchemaRule = (iterativeUpdatesEnabled: boolean = fals * @param currentBuild The last deployed build. * @param nextBuild The next build. */ -export const getCantAddLSILaterRule = (iterativeUpdatesEnabled: boolean = false) => { - const cantAddLSILaterRule = (diff: Diff): void => { - if ( - // When adding a LSI to a table that has 0 LSIs. - (diff.kind === 'N' && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes') || - // When adding a LSI to a table that already has at least one LSI. - (diff.kind === 'A' && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes' && diff.item.kind === 'N') - ) { - // diff.path = [ "stacks", "Todo.json", "Resources", "TodoTable", "Properties", "LocalSecondaryIndexes" ] - const stackName = path.basename(diff.path[1], '.json'); - const tableName = diff.path[3]; - - if (iterativeUpdatesEnabled) { - throw new DestructiveMigrationError( - 'Adding an LSI to a model requires replacement of the underlying DynamoDB table.', - [], - [tableName], - ); - } +export const cantAddLSILaterRule = (diff: Diff): void => { + if ( + // When adding a LSI to a table that has 0 LSIs. + (diff.kind === 'N' && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes') || + // When adding a LSI to a table that already has at least one LSI. + (diff.kind === 'A' && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes' && diff.item.kind === 'N') + ) { + // diff.path = [ "stacks", "Todo.json", "Resources", "TodoTable", "Properties", "LocalSecondaryIndexes" ] + const stackName = path.basename(diff.path[1], '.json'); + const tableName = diff.path[3]; - throw new InvalidMigrationError( - `Attempting to add a local secondary index to the ${tableName} table in the ${stackName} stack. ` + - 'Local secondary indexes must be created when the table is created.', - "Adding a @key directive where the first field in 'fields' is the same as the first field in the 'fields' of the primary @key.", - "Change the first field in 'fields' such that a global secondary index is created or delete and recreate the model.", - ); - } - }; - return cantAddLSILaterRule; + throw new InvalidMigrationError( + `Attempting to add a local secondary index to the ${tableName} table in the ${stackName} stack. ` + + 'Local secondary indexes must be created when the table is created.', + "Adding a @key directive where the first field in 'fields' is the same as the first field in the 'fields' of the primary @key.", + "Change the first field in 'fields' such that a global secondary index is created or delete and recreate the model.", + ); + } }; /** @@ -317,78 +295,65 @@ export const cantMutateMultipleGSIAtUpdateTimeRule = (diffs: Diff[], currentBuil * @param currentBuild The last deployed build. * @param nextBuild The next build. */ -export const getCantEditLSIKeySchemaRule = (iterativeUpdatesEnabled: boolean = false) => { - const cantEditLSIKeySchemaRule = (diff: Diff, currentBuild: DiffableProject, nextBuild: DiffableProject): void => { - if ( - // ["stacks","Todo.json","Resources","TodoTable","Properties","LocalSecondaryIndexes",0,"KeySchema",0,"AttributeName"] - diff.kind === 'E' && - diff.path.length === 10 && - diff.path[5] === 'LocalSecondaryIndexes' && - diff.path[7] === 'KeySchema' - ) { - // This error is symptomatic of a change to the GSI array but does not necessarily imply a breaking change. - const pathToGSIs = diff.path.slice(0, 6); - const oldIndexes = _.get(currentBuild, pathToGSIs); - const newIndexes = _.get(nextBuild, pathToGSIs); - const oldIndexesDiffable = _.keyBy(oldIndexes, 'IndexName'); - const newIndexesDiffable = _.keyBy(newIndexes, 'IndexName'); - const innerDiffs = getDiffs(oldIndexesDiffable, newIndexesDiffable) || []; - - // We must look at this inner diff or else we could confuse a situation - // where the user adds a LSI to the beginning of the LocalSecondaryIndex list in CFN. - // We re-key the indexes list so we can determine if a change occurred to an index that - // already exists. - for (const innerDiff of innerDiffs) { - // path: ["AGSI","KeySchema",0,"AttributeName"] - if (innerDiff.kind === 'E' && innerDiff.path.length > 2 && innerDiff.path[1] === 'KeySchema') { - const indexName = innerDiff.path[0]; - const stackName = path.basename(diff.path[1], '.json'); - const tableName = diff.path[3]; - - if (iterativeUpdatesEnabled) { - throw new DestructiveMigrationError('Editing an LSI requires replacement of the underlying DynamoDB table.', [], [tableName]); - } - - throw new InvalidMigrationError( - `Attempting to edit the local secondary index ${indexName} on the ${tableName} table in the ${stackName} stack. `, - 'The key schema of a local secondary index cannot be changed after being deployed.', - 'When enabling new access patterns you should: 1. Add a new @key 2. run amplify push ' + - '3. Verify the new access pattern and remove the old @key.', - ); - } +export const cantEditLSIKeySchemaRule = (diff: Diff, currentBuild: DiffableProject, nextBuild: DiffableProject): void => { + if ( + // ["stacks","Todo.json","Resources","TodoTable","Properties","LocalSecondaryIndexes",0,"KeySchema",0,"AttributeName"] + diff.kind === 'E' && + diff.path.length === 10 && + diff.path[5] === 'LocalSecondaryIndexes' && + diff.path[7] === 'KeySchema' + ) { + // This error is symptomatic of a change to the GSI array but does not necessarily imply a breaking change. + const pathToGSIs = diff.path.slice(0, 6); + const oldIndexes = _.get(currentBuild, pathToGSIs); + const newIndexes = _.get(nextBuild, pathToGSIs); + const oldIndexesDiffable = _.keyBy(oldIndexes, 'IndexName'); + const newIndexesDiffable = _.keyBy(newIndexes, 'IndexName'); + const innerDiffs = getDiffs(oldIndexesDiffable, newIndexesDiffable) || []; + + // We must look at this inner diff or else we could confuse a situation + // where the user adds a LSI to the beginning of the LocalSecondaryIndex list in CFN. + // We re-key the indexes list so we can determine if a change occurred to an index that + // already exists. + for (const innerDiff of innerDiffs) { + // path: ["AGSI","KeySchema",0,"AttributeName"] + if (innerDiff.kind === 'E' && innerDiff.path.length > 2 && innerDiff.path[1] === 'KeySchema') { + const indexName = innerDiff.path[0]; + const stackName = path.basename(diff.path[1], '.json'); + const tableName = diff.path[3]; + + throw new InvalidMigrationError( + `Attempting to edit the local secondary index ${indexName} on the ${tableName} table in the ${stackName} stack. `, + 'The key schema of a local secondary index cannot be changed after being deployed.', + 'When enabling new access patterns you should: 1. Add a new @key 2. run amplify push ' + + '3. Verify the new access pattern and remove the old @key.', + ); } } - }; - return cantEditLSIKeySchemaRule; + } }; -export const getCantRemoveLSILater = (iterativeUpdatesEnabled: boolean = false) => { - const cantRemoveLSILater = (diff: Diff, currentBuild: DiffableProject, nextBuild: DiffableProject) => { - const throwError = (stackName: string, tableName: string): void => { - if (iterativeUpdatesEnabled) { - throw new DestructiveMigrationError('Removing an LSI requires replacement of the underlying DynamoDB table.', [], [tableName]); - } - throw new InvalidMigrationError( - `Attempting to remove a local secondary index on the ${tableName} table in the ${stackName} stack.`, - 'A local secondary index cannot be removed after deployment.', - 'In order to remove the local secondary index you need to delete or rename the table.', - ); - }; - // if removing more than one lsi - if (diff.kind === 'D' && diff.lhs && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes') { - const tableName = diff.path[3]; - const stackName = path.basename(diff.path[1], '.json'); - throwError(stackName, tableName); - } - // if removing one lsi - if (diff.kind === 'A' && diff.item.kind === 'D' && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes') { - const tableName = diff.path[3]; - const stackName = path.basename(diff.path[1], '.json'); - throwError(stackName, tableName); - } +export function cantRemoveLSILater(diff: Diff, currentBuild: DiffableProject, nextBuild: DiffableProject) { + const throwError = (stackName: string, tableName: string): void => { + throw new InvalidMigrationError( + `Attempting to remove a local secondary index on the ${tableName} table in the ${stackName} stack.`, + 'A local secondary index cannot be removed after deployment.', + 'In order to remove the local secondary index you need to delete or rename the table.', + ); }; - return cantRemoveLSILater; -}; + // if removing more than one lsi + if (diff.kind === 'D' && diff.lhs && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes') { + const tableName = diff.path[3]; + const stackName = path.basename(diff.path[1], '.json'); + throwError(stackName, tableName); + } + // if removing one lsi + if (diff.kind === 'A' && diff.item.kind === 'D' && diff.path.length === 6 && diff.path[5] === 'LocalSecondaryIndexes') { + const tableName = diff.path[3]; + const stackName = path.basename(diff.path[1], '.json'); + throwError(stackName, tableName); + } +} export const cantHaveMoreThan500ResourcesRule = (diffs: Diff[], currentBuild: DiffableProject, nextBuild: DiffableProject): void => { const stackKeys = Object.keys(nextBuild.stacks); @@ -408,23 +373,6 @@ export const cantHaveMoreThan500ResourcesRule = (diffs: Diff[], currentBuild: Di } }; -export const cantRemoveTableAfterCreation = (_: Diff, currentBuild: DiffableProject, nextBuild: DiffableProject): void => { - const getNestedStackLogicalIds = (proj: DiffableProject) => - Object.entries(proj.root.Resources || []) - .filter(([_, meta]) => meta.Type === 'AWS::CloudFormation::Stack') - .map(([name]) => name); - const currentModels = getNestedStackLogicalIds(currentBuild); - const nextModels = getNestedStackLogicalIds(nextBuild); - const removedModels = currentModels.filter(currModel => !nextModels.includes(currModel)); - if (removedModels.length > 0) { - throw new DestructiveMigrationError( - 'Removing a model from the GraphQL schema will also remove the underlying DynamoDB table.', - removedModels, - [], - ); - } -}; - const loadDiffableProject = async (path: string, rootStackName: string): Promise => { const project = await readFromPath(path); const currentStacks = project.stacks || {}; diff --git a/packages/graphql-transformers-e2e-tests/CHANGELOG.md b/packages/graphql-transformers-e2e-tests/CHANGELOG.md index 721e266103a..79ebbbe7be4 100644 --- a/packages/graphql-transformers-e2e-tests/CHANGELOG.md +++ b/packages/graphql-transformers-e2e-tests/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [6.27.0-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-transformers-e2e-tests@6.26.3...graphql-transformers-e2e-tests@6.27.0-graphql-vnext-dev-preview.0) (2021-09-27) + + +### Bug Fixes + +* **Searchable:** confirm ES data propagate ([#8251](https://github.com/aws-amplify/amplify-cli/issues/8251)) ([8e5c791](https://github.com/aws-amplify/amplify-cli/commit/8e5c7917d459c1eb53228884dcae1e395c125c18)) + + +### Features + +* add [@auth](https://github.com/auth) ([#1](https://github.com/aws-amplify/amplify-cli/issues/1)) ([7c13d99](https://github.com/aws-amplify/amplify-cli/commit/7c13d99a15e811efeff32ed061573d63ee9093ba)), closes [#8074](https://github.com/aws-amplify/amplify-cli/issues/8074) [#8078](https://github.com/aws-amplify/amplify-cli/issues/8078) [#8124](https://github.com/aws-amplify/amplify-cli/issues/8124) [#8146](https://github.com/aws-amplify/amplify-cli/issues/8146) [#8168](https://github.com/aws-amplify/amplify-cli/issues/8168) [#8138](https://github.com/aws-amplify/amplify-cli/issues/8138) [#8174](https://github.com/aws-amplify/amplify-cli/issues/8174) [#8175](https://github.com/aws-amplify/amplify-cli/issues/8175) [#8180](https://github.com/aws-amplify/amplify-cli/issues/8180) [#8179](https://github.com/aws-amplify/amplify-cli/issues/8179) [#8234](https://github.com/aws-amplify/amplify-cli/issues/8234) [#8246](https://github.com/aws-amplify/amplify-cli/issues/8246) [#8248](https://github.com/aws-amplify/amplify-cli/issues/8248) [#8259](https://github.com/aws-amplify/amplify-cli/issues/8259) +* add [@many](https://github.com/many)ToMany directive ([#8195](https://github.com/aws-amplify/amplify-cli/issues/8195)) ([cc644eb](https://github.com/aws-amplify/amplify-cli/commit/cc644ebc4968f29ad6b3f0b42013d7ee6a142f7e)) + + + + + ## [6.26.3](https://github.com/aws-amplify/amplify-cli/compare/graphql-transformers-e2e-tests@6.26.2...graphql-transformers-e2e-tests@6.26.3) (2021-09-18) **Note:** Version bump only for package graphql-transformers-e2e-tests diff --git a/packages/graphql-transformers-e2e-tests/package.json b/packages/graphql-transformers-e2e-tests/package.json index c0db8b7d445..b2f64b1fd9a 100644 --- a/packages/graphql-transformers-e2e-tests/package.json +++ b/packages/graphql-transformers-e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "graphql-transformers-e2e-tests", - "version": "6.26.3", + "version": "6.27.0-graphql-vnext-dev-preview.0", "description": "End to end functional tests for appsync supported transformers.", "private": true, "repository": { @@ -25,15 +25,16 @@ "axios": "^0.21.4", "cloudform-types": "^4.2.0", "graphql": "^14.5.8", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0", "moment": "^2.24.0" }, "devDependencies": { "@aws-amplify/core": "^2.1.0", - "@aws-amplify/graphql-index-transformer": "0.3.2", - "@aws-amplify/graphql-model-transformer": "0.6.2", - "@aws-amplify/graphql-transformer-core": "0.9.0", + "@aws-amplify/graphql-index-transformer": "0.4.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-model-transformer": "0.7.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-core": "0.10.0-graphql-vnext-dev-preview.0", + "@aws-amplify/graphql-transformer-interfaces": "1.10.0-graphql-vnext-dev-preview.0", "@types/node": "^12.12.6", "aws-amplify": "^4.2.8", "aws-appsync": "^4.1.1", @@ -41,16 +42,16 @@ "aws-sdk": "^2.963.0", "execa": "^5.1.1", "fs-extra": "^8.1.0", - "graphql-auth-transformer": "6.24.22", - "graphql-connection-transformer": "4.21.22", - "graphql-dynamodb-transformer": "6.22.22", - "graphql-elasticsearch-transformer": "4.12.1", - "graphql-function-transformer": "2.5.21", - "graphql-http-transformer": "4.18.9", - "graphql-key-transformer": "2.23.22", - "graphql-predictions-transformer": "2.5.21", + "graphql-auth-transformer": "6.25.0-graphql-vnext-dev-preview.0", + "graphql-connection-transformer": "4.21.23-graphql-vnext-dev-preview.0", + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0", + "graphql-elasticsearch-transformer": "4.12.2-graphql-vnext-dev-preview.0", + "graphql-function-transformer": "2.5.22-graphql-vnext-dev-preview.0", + "graphql-http-transformer": "4.18.10-graphql-vnext-dev-preview.0", + "graphql-key-transformer": "2.23.23-graphql-vnext-dev-preview.0", + "graphql-predictions-transformer": "2.5.22-graphql-vnext-dev-preview.0", "graphql-tag": "^2.10.1", - "graphql-versioned-transformer": "4.17.22", + "graphql-versioned-transformer": "4.17.23-graphql-vnext-dev-preview.0", "isomorphic-fetch": "^3.0.0", "jest-junit": "^12.0.0", "node-fetch": "^2.6.1", diff --git a/packages/graphql-transformers-e2e-tests/src/__tests__/AuthV2Transformer.e2e.test.ts b/packages/graphql-transformers-e2e-tests/src/__tests__/AuthV2Transformer.e2e.test.ts new file mode 100644 index 00000000000..5721d076abd --- /dev/null +++ b/packages/graphql-transformers-e2e-tests/src/__tests__/AuthV2Transformer.e2e.test.ts @@ -0,0 +1,3329 @@ +import { IndexTransformer, PrimaryKeyTransformer } from '@aws-amplify/graphql-index-transformer'; +import { HasOneTransformer } from '@aws-amplify/graphql-relational-transformer'; +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces'; +import { AuthTransformer } from '@aws-amplify/graphql-auth-transformer'; +import { Output } from 'aws-sdk/clients/cloudformation'; +import { CloudFormationClient } from '../CloudFormationClient'; +import { S3Client } from '../S3Client'; +import { cleanupStackAfterTest, deploy } from '../deployNestedStacks'; +import { default as CognitoClient } from 'aws-sdk/clients/cognitoidentityserviceprovider'; +import { default as S3 } from 'aws-sdk/clients/s3'; +import moment from 'moment'; +import { + createUserPool, + createUserPoolClient, + configureAmplify, + addUserToGroup, + authenticateUser, + createGroup, + signupUser, +} from '../cognitoUtils'; +import { ResourceConstants } from 'graphql-transformer-common'; +import { GraphQLClient } from '../GraphQLClient'; + +jest.setTimeout(2000000); + +describe('@model with @auth', () => { + // setup clients + const cf = new CloudFormationClient('us-west-2'); + const customS3Client = new S3Client('us-west-2'); + const cognitoClient = new CognitoClient({ apiVersion: '2016-04-19', region: 'us-west-2' }); + const awsS3Client = new S3({ region: 'us-west-2' }); + + // stack info + const BUILD_TIMESTAMP = moment().format('YYYYMMDDHHmmss'); + const STACK_NAME = `AuthV2TransformerTests-${BUILD_TIMESTAMP}`; + const BUCKET_NAME = `appsync-auth-v2-transformer-test-bucket-${BUILD_TIMESTAMP}`; + const LOCAL_FS_BUILD_DIR = '/tmp/authv2_transformer_tests/'; + const S3_ROOT_DIR_KEY = 'deployments'; + let USER_POOL_ID: string; + let GRAPHQL_ENDPOINT: string; + /** + * Client 1 is logged in and is a member of the Admin group. + */ + let GRAPHQL_CLIENT_1: GraphQLClient; + + /** + * Client 1 is logged in and is a member of the Admin group via an access token. + */ + let GRAPHQL_CLIENT_1_ACCESS: GraphQLClient; + + /** + * Client 2 is logged in and is a member of the Devs group. + */ + let GRAPHQL_CLIENT_2: GraphQLClient; + + /** + * Client 3 is logged in and is a member of the Devs-Admin group via an access token. + */ + let GRAPHQL_CLIENT_3: GraphQLClient; + + // env info + const USERNAME1 = 'user1@test.com'; + const USERNAME2 = 'user2@test.com'; + const USERNAME3 = 'user3@test.com'; + const TMP_PASSWORD = 'Password123!'; + const REAL_PASSWORD = 'Password1234!'; + + const ADMIN_GROUP_NAME = 'Admin'; + const DEVS_GROUP_NAME = 'Devs'; + const DEVS_ADMIN_GROUP_NAME = 'Devs-Admin'; + const PARTICIPANT_GROUP_NAME = 'Participant'; + const WATCHER_GROUP_NAME = 'Watcher'; + + function outputValueSelector(key: string) { + return (outputs: Output[]) => { + const output = outputs.find((o: Output) => o.OutputKey === key); + return output ? output.OutputValue : null; + }; + } + beforeAll(async () => { + const validSchema = ` + type Post @model @auth(rules: [{ allow: owner }]) { + id: ID! + title: String! + createdAt: AWSDateTime + updatedAt: AWSDateTime + owner: String + } + type Salary + @model + @auth(rules: [{ allow: owner }, { allow: groups, groups: ["Admin"] }]) { + id: ID! + wage: Int + owner: String + } + type AdminNote + @model + @auth( + rules: [{ allow: groups, groups: ["Admin"], groupClaim: "cognito:groups" }] + ) { + id: ID! + content: String! + } + type ManyGroupProtected + @model + @auth(rules: [{ allow: groups, groupsField: "groups" }]) { + id: ID! + value: Int + groups: [String] + } + type SingleGroupProtected + @model + @auth(rules: [{ allow: groups, groupsField: "group" }]) { + id: ID! + value: Int + group: String + } + type PWProtected + @auth( + rules: [ + { + allow: groups + groups: ["Devs"] + } + { + allow: groups + groupsField: "participants" + operations: [read, update, delete] + } + { allow: groups, groupsField: "watchers", operations: [read] } + ] + ) + @model { + id: ID! + content: String! + participants: String + watchers: String + } + type AllThree + @auth( + rules: [ + { allow: owner, identityClaim: "username" } + { allow: owner, ownerField: "editors", identityClaim: "cognito:username" } + { allow: groups, groups: ["Admin", "Execs"] } + { allow: groups, groupsField: "groups" } + { allow: groups, groupsField: "alternativeGroup" } + ] + ) + @model { + id: ID! + owner: String + editors: [String] + groups: [String] + alternativeGroup: String + } + # The owner should always start with https://cognito-idp + type TestIdentity + @model + @auth(rules: [{ allow: owner, identityClaim: "iss" }]) { + id: ID! + title: String! + owner: String + } + type OwnerReadProtected + @model + @auth(rules: [{ allow: groups, groups: ["Admin"]},{ allow: owner, operations: [read] }]) { + id: ID! @primaryKey(sortKeyFields: ["sk"]) + sk: String! + content: String + owner: String + } + type OwnerCreateUpdateDeleteProtected + @model + @auth(rules: [{ allow: owner, operations: [create, update, delete] }]) { + id: ID! + content: String + owner: String + } + type Performance + @model + @auth( + rules: [ + { allow: groups, groups: ["Admin"] } + { allow: private, operations: [read] } + ] + ) { + id: ID + performer: String! + description: String! + time: AWSDateTime + stage: Stage @hasOne + } + type Stage + @model + @auth( + rules: [ + { allow: groups, groups: ["Admin"] } + { allow: private, operations: [read] } + ] + ) { + id: ID! + name: String! + }`; + try { + await awsS3Client.createBucket({ Bucket: BUCKET_NAME }).promise(); + } catch (e) { + throw Error(`Could not create bucket: ${e}`); + } + const authConfig: AppSyncAuthConfiguration = { + defaultAuthentication: { + authenticationType: 'AMAZON_COGNITO_USER_POOLS', + }, + additionalAuthenticationProviders: [], + }; + const transformer = new GraphQLTransform({ + authConfig, + transformers: [ + new ModelTransformer(), + new PrimaryKeyTransformer(), + new IndexTransformer(), + new HasOneTransformer(), + new AuthTransformer({ + authConfig, + addAwsIamAuthInOutputSchema: false, + }), + ], + }); + const userPoolResponse = await createUserPool(cognitoClient, `UserPool${STACK_NAME}`); + USER_POOL_ID = userPoolResponse.UserPool.Id; + const userPoolClientResponse = await createUserPoolClient(cognitoClient, USER_POOL_ID, `UserPool${STACK_NAME}`); + const userPoolClientId = userPoolClientResponse.UserPoolClient.ClientId; + try { + const out = transformer.transform(validSchema); + const finishedStack = await deploy( + customS3Client, + cf, + STACK_NAME, + out, + { AuthCognitoUserPoolId: USER_POOL_ID }, + LOCAL_FS_BUILD_DIR, + BUCKET_NAME, + S3_ROOT_DIR_KEY, + BUILD_TIMESTAMP, + ); + // Wait for any propagation to avoid random + // "The security token included in the request is invalid" errors + await new Promise(res => setTimeout(() => res(), 5000)); + + expect(finishedStack).toBeDefined(); + const getApiEndpoint = outputValueSelector(ResourceConstants.OUTPUTS.GraphQLAPIEndpointOutput); + const getApiKey = outputValueSelector(ResourceConstants.OUTPUTS.GraphQLAPIApiKeyOutput); + GRAPHQL_ENDPOINT = getApiEndpoint(finishedStack.Outputs); + + const apiKey = getApiKey(finishedStack.Outputs); + expect(apiKey).not.toBeTruthy(); + + // Verify we have all the details + expect(GRAPHQL_ENDPOINT).toBeTruthy(); + expect(USER_POOL_ID).toBeTruthy(); + expect(userPoolClientId).toBeTruthy(); + + // Configure Amplify, create users, and sign in + configureAmplify(USER_POOL_ID, userPoolClientId); + + await signupUser(USER_POOL_ID, USERNAME1, TMP_PASSWORD); + await signupUser(USER_POOL_ID, USERNAME2, TMP_PASSWORD); + await signupUser(USER_POOL_ID, USERNAME3, TMP_PASSWORD); + await createGroup(USER_POOL_ID, ADMIN_GROUP_NAME); + await createGroup(USER_POOL_ID, PARTICIPANT_GROUP_NAME); + await createGroup(USER_POOL_ID, WATCHER_GROUP_NAME); + await createGroup(USER_POOL_ID, DEVS_GROUP_NAME); + await createGroup(USER_POOL_ID, DEVS_ADMIN_GROUP_NAME); + await addUserToGroup(ADMIN_GROUP_NAME, USERNAME1, USER_POOL_ID); + await addUserToGroup(PARTICIPANT_GROUP_NAME, USERNAME1, USER_POOL_ID); + await addUserToGroup(WATCHER_GROUP_NAME, USERNAME1, USER_POOL_ID); + await addUserToGroup(DEVS_GROUP_NAME, USERNAME2, USER_POOL_ID); + await addUserToGroup(DEVS_ADMIN_GROUP_NAME, USERNAME3, USER_POOL_ID); + const authResAfterGroup: any = await authenticateUser(USERNAME1, TMP_PASSWORD, REAL_PASSWORD); + + const idToken = authResAfterGroup.getIdToken().getJwtToken(); + GRAPHQL_CLIENT_1 = new GraphQLClient(GRAPHQL_ENDPOINT, { Authorization: idToken }); + + const accessToken = authResAfterGroup.getAccessToken().getJwtToken(); + GRAPHQL_CLIENT_1_ACCESS = new GraphQLClient(GRAPHQL_ENDPOINT, { Authorization: accessToken }); + + const authRes2AfterGroup: any = await authenticateUser(USERNAME2, TMP_PASSWORD, REAL_PASSWORD); + const idToken2 = authRes2AfterGroup.getIdToken().getJwtToken(); + GRAPHQL_CLIENT_2 = new GraphQLClient(GRAPHQL_ENDPOINT, { Authorization: idToken2 }); + + const authRes3AfterGroup: any = await authenticateUser(USERNAME3, TMP_PASSWORD, REAL_PASSWORD); + const idToken3 = authRes3AfterGroup.getIdToken().getJwtToken(); + GRAPHQL_CLIENT_3 = new GraphQLClient(GRAPHQL_ENDPOINT, { Authorization: idToken3 }); + } catch (e) { + console.error(`Could not setup transformer ${e}`); + expect(true).toBe(false); + } + }); + + afterAll(async () => { + await cleanupStackAfterTest(BUCKET_NAME, STACK_NAME, cf, { cognitoClient, userPoolId: USER_POOL_ID }); + }); + + /** + * Test queries below + */ + test('Test createPost mutation', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toEqual(USERNAME1); + + const response2 = await GRAPHQL_CLIENT_1_ACCESS.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response2.data.createPost.id).toBeDefined(); + expect(response2.data.createPost.title).toEqual('Hello, World!'); + expect(response2.data.createPost.createdAt).toBeDefined(); + expect(response2.data.createPost.updatedAt).toBeDefined(); + expect(response2.data.createPost.owner).toEqual(USERNAME1); + }); + + test('Test getPost query when authorized', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toEqual(USERNAME1); + const getResponse = await GRAPHQL_CLIENT_1.query( + `query { + getPost(id: "${response.data.createPost.id}") { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(getResponse.data.getPost.id).toBeDefined(); + expect(getResponse.data.getPost.title).toEqual('Hello, World!'); + expect(getResponse.data.getPost.createdAt).toBeDefined(); + expect(getResponse.data.getPost.updatedAt).toBeDefined(); + expect(getResponse.data.getPost.owner).toEqual(USERNAME1); + + const getResponseAccess = await GRAPHQL_CLIENT_1_ACCESS.query( + `query { + getPost(id: "${response.data.createPost.id}") { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(getResponseAccess.data.getPost.id).toBeDefined(); + expect(getResponseAccess.data.getPost.title).toEqual('Hello, World!'); + expect(getResponseAccess.data.getPost.createdAt).toBeDefined(); + expect(getResponseAccess.data.getPost.updatedAt).toBeDefined(); + expect(getResponseAccess.data.getPost.owner).toEqual(USERNAME1); + }); + + test('Test getPost query when not authorized', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toBeDefined(); + const getResponse = await GRAPHQL_CLIENT_2.query( + `query { + getPost(id: "${response.data.createPost.id}") { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(getResponse.data.getPost).toEqual(null); + expect(getResponse.errors.length).toEqual(1); + expect((getResponse.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test('Test updatePost mutation when authorized', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toEqual(USERNAME1); + const updateResponse = await GRAPHQL_CLIENT_1.query( + `mutation { + updatePost(input: { id: "${response.data.createPost.id}", title: "Bye, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(updateResponse.data.updatePost.id).toEqual(response.data.createPost.id); + expect(updateResponse.data.updatePost.title).toEqual('Bye, World!'); + expect(updateResponse.data.updatePost.updatedAt > response.data.createPost.updatedAt).toEqual(true); + + const updateResponseAccess = await GRAPHQL_CLIENT_1_ACCESS.query( + `mutation { + updatePost(input: { id: "${response.data.createPost.id}", title: "Bye, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(updateResponseAccess.data.updatePost.id).toEqual(response.data.createPost.id); + expect(updateResponseAccess.data.updatePost.title).toEqual('Bye, World!'); + expect(updateResponseAccess.data.updatePost.updatedAt > response.data.createPost.updatedAt).toEqual(true); + }); + + test('Test updatePost mutation when not authorized', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toBeDefined(); + const updateResponse = await GRAPHQL_CLIENT_2.query( + `mutation { + updatePost(input: { id: "${response.data.createPost.id}", title: "Bye, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(updateResponse.data.updatePost).toEqual(null); + expect(updateResponse.errors.length).toEqual(1); + expect((updateResponse.errors[0] as any).data).toBeNull(); + expect((updateResponse.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test('Test deletePost mutation when authorized', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toEqual(USERNAME1); + const deleteResponse = await GRAPHQL_CLIENT_1.query( + `mutation { + deletePost(input: { id: "${response.data.createPost.id}" }) { + id + } + }`, + {}, + ); + expect(deleteResponse.data.deletePost.id).toEqual(response.data.createPost.id); + + const responseAccess = await GRAPHQL_CLIENT_1_ACCESS.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(responseAccess.data.createPost.id).toBeDefined(); + expect(responseAccess.data.createPost.title).toEqual('Hello, World!'); + expect(responseAccess.data.createPost.createdAt).toBeDefined(); + expect(responseAccess.data.createPost.updatedAt).toBeDefined(); + expect(responseAccess.data.createPost.owner).toEqual(USERNAME1); + const deleteResponseAccess = await GRAPHQL_CLIENT_1_ACCESS.query( + `mutation { + deletePost(input: { id: "${responseAccess.data.createPost.id}" }) { + id + } + }`, + {}, + ); + expect(deleteResponseAccess.data.deletePost.id).toEqual(responseAccess.data.createPost.id); + }); + + test('Test deletePost mutation when not authorized', async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "Hello, World!" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(response.data.createPost.id).toBeDefined(); + expect(response.data.createPost.title).toEqual('Hello, World!'); + expect(response.data.createPost.createdAt).toBeDefined(); + expect(response.data.createPost.updatedAt).toBeDefined(); + expect(response.data.createPost.owner).toEqual(USERNAME1); + const deleteResponse = await GRAPHQL_CLIENT_2.query( + `mutation { + deletePost(input: { id: "${response.data.createPost.id}" }) { + id + } + }`, + {}, + ); + expect(deleteResponse.data.deletePost).toEqual(null); + expect(deleteResponse.errors.length).toEqual(1); + expect((deleteResponse.errors[0] as any).data).toBeNull(); + expect((deleteResponse.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test('Test listPosts query when authorized', async () => { + const firstPost = await GRAPHQL_CLIENT_1.query( + `mutation { + createPost(input: { title: "testing list" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + expect(firstPost.data.createPost.id).toBeDefined(); + expect(firstPost.data.createPost.title).toEqual('testing list'); + expect(firstPost.data.createPost.createdAt).toBeDefined(); + expect(firstPost.data.createPost.updatedAt).toBeDefined(); + expect(firstPost.data.createPost.owner).toEqual(USERNAME1); + await GRAPHQL_CLIENT_2.query( + `mutation { + createPost(input: { title: "testing list" }) { + id + title + createdAt + updatedAt + owner + } + }`, + {}, + ); + // There are two posts but only 1 created by me. + const listResponse = await GRAPHQL_CLIENT_1.query( + `query { + listPosts(filter: { title: { eq: "testing list" } }, limit: 25) { + items { + id + } + } + }`, + {}, + ); + expect(listResponse.data.listPosts.items.length).toEqual(1); + + const listResponseAccess = await GRAPHQL_CLIENT_1_ACCESS.query( + `query { + listPosts(filter: { title: { eq: "testing list" } }, limit: 25) { + items { + id + } + } + }`, + {}, + ); + expect(listResponseAccess.data.listPosts.items.length).toEqual(1); + }); + + /** + * Static Group Auth + */ + test(`Test createSalary w/ Admin group protection authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 10 }) { + id + wage + } + } + `, + {}, + ); + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(10); + }); + + test(`Test update my own salary without admin permission`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createSalary(input: { wage: 10 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.wage).toEqual(10); + const req2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateSalary(input: { id: "${req.data.createSalary.id}", wage: 14 }) { + id + wage + } + } + `, + {}, + ); + + expect(req2.data.updateSalary.wage).toEqual(14); + }); + + test(`Test updating someone else's salary as an admin`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createSalary(input: { wage: 11 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(11); + const req2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + updateSalary(input: { id: "${req.data.createSalary.id}", wage: 12 }) { + id + wage + } + } + `, + {}, + ); + + expect(req2.data.updateSalary.id).toEqual(req.data.createSalary.id); + expect(req2.data.updateSalary.wage).toEqual(12); + }); + + test(`Test updating someone else's salary when I am not admin.`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 13 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(13); + const req2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateSalary(input: { id: "${req.data.createSalary.id}", wage: 14 }) { + id + wage + } + } + `, + {}, + ); + expect(req2.data.updateSalary).toEqual(null); + expect(req2.errors.length).toEqual(1); + expect((req2.errors[0] as any).data).toBeNull(); + expect((req2.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test deleteSalary w/ Admin group protection authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 15 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(15); + const req2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteSalary(input: { id: "${req.data.createSalary.id}" }) { + id + wage + } + } + `, + {}, + ); + + expect(req2.data.deleteSalary.id).toEqual(req.data.createSalary.id); + expect(req2.data.deleteSalary.wage).toEqual(15); + }); + + test(`Test deleteSalary w/ Admin group protection not authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 16 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(16); + const req2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + deleteSalary(input: { id: "${req.data.createSalary.id}" }) { + id + wage + } + } + `, + {}, + ); + expect(req2.data.deleteSalary).toEqual(null); + expect(req2.errors.length).toEqual(1); + expect((req2.errors[0] as any).data).toBeNull(); + expect((req2.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test and Admin can get a salary created by any user`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createSalary(input: { wage: 15 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(15); + const req2 = await GRAPHQL_CLIENT_1.query( + ` + query { + getSalary(id: "${req.data.createSalary.id}") { + id + wage + } + } + `, + {}, + ); + expect(req2.data.getSalary.id).toEqual(req.data.createSalary.id); + expect(req2.data.getSalary.wage).toEqual(15); + }); + + test(`Test owner can create and get a salary when not admin`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createSalary(input: { wage: 15 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(15); + const req2 = await GRAPHQL_CLIENT_2.query( + ` + query { + getSalary(id: "${req.data.createSalary.id}") { + id + wage + } + } + `, + {}, + ); + expect(req2.data.getSalary.id).toEqual(req.data.createSalary.id); + expect(req2.data.getSalary.wage).toEqual(15); + }); + + test(`Test getSalary w/ Admin group protection not authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 16 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(16); + const req2 = await GRAPHQL_CLIENT_2.query( + ` + query { + getSalary(id: "${req.data.createSalary.id}") { + id + wage + } + } + `, + {}, + ); + expect(req2.data.getSalary).toEqual(null); + expect(req2.errors.length).toEqual(1); + expect((req2.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test listSalaries w/ Admin group protection authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 101 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(101); + const req2 = await GRAPHQL_CLIENT_1.query( + ` + query { + listSalaries(filter: { wage: { eq: 101 }}) { + items { + id + wage + } + } + } + `, + {}, + ); + expect(req2.data.listSalaries.items.length).toEqual(1); + expect(req2.data.listSalaries.items[0].id).toEqual(req.data.createSalary.id); + expect(req2.data.listSalaries.items[0].wage).toEqual(101); + }); + + test(`Test listSalaries w/ Admin group protection not authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSalary(input: { wage: 102 }) { + id + wage + } + } + `, + {}, + ); + + expect(req.data.createSalary.id).toBeDefined(); + expect(req.data.createSalary.wage).toEqual(102); + const req2 = await GRAPHQL_CLIENT_2.query( + ` + query { + listSalaries(filter: { wage: { eq: 102 }}) { + items { + id + wage + } + } + } + `, + {}, + ); + expect(req2.data.listSalaries.items).toEqual([]); + }); + + /** + * Dynamic Group Auth + */ + test(`Test createManyGroupProtected w/ dynamic group protection authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createManyGroupProtected(input: { value: 10, groups: ["Admin"] }) { + id + value + groups + } + } + `, + {}, + ); + + expect(req.data.createManyGroupProtected.id).toBeDefined(); + expect(req.data.createManyGroupProtected.value).toEqual(10); + expect(req.data.createManyGroupProtected.groups).toEqual(['Admin']); + }); + + test(`Test createManyGroupProtected w/ dynamic group protection when not authorized`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createManyGroupProtected(input: { value: 10, groups: ["Admin"] }) { + id + value + groups + } + } + `, + {}, + ); + + expect(req.data.createManyGroupProtected).toEqual(null); + expect(req.errors.length).toEqual(1); + expect((req.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test updateSingleGroupProtected when user is not authorized but has a group that is a substring of the allowed group`, async () => { + const req = await GRAPHQL_CLIENT_3.query( + `mutation { + createSingleGroupProtected(input: { value: 11, group: "Devs-Admin" }) { + id + value + group + } + } + `, + {}, + ); + + const req2 = await GRAPHQL_CLIENT_2.query( + `mutation { + updateSingleGroupProtected(input: {id: "${req.data.createSingleGroupProtected.id}", value: 5 }) { + id + value + group + } + } + `, + {}, + ); + + const req3 = await GRAPHQL_CLIENT_3.query( + `query { + getSingleGroupProtected(id: "${req.data.createSingleGroupProtected.id}") { + id + value + group + } + } + `, + {}, + ); + + expect(req.data.createSingleGroupProtected.value).toEqual(11); + expect(req.data.createSingleGroupProtected.value).toEqual(req3.data.getSingleGroupProtected.value); + expect((req2.errors[0] as any).data).toBeNull(); + expect((req2.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test createSingleGroupProtected w/ dynamic group protection authorized`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createSingleGroupProtected(input: { value: 10, group: "Admin" }) { + id + value + group + } + } + `, + {}, + ); + + expect(req.data.createSingleGroupProtected.id).toBeDefined(); + expect(req.data.createSingleGroupProtected.value).toEqual(10); + expect(req.data.createSingleGroupProtected.group).toEqual('Admin'); + }); + + test(`Test createSingleGroupProtected w/ dynamic group protection when not authorized`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createSingleGroupProtected(input: { value: 10, group: "Admin" }) { + id + value + group + } + } + `, + {}, + ); + + expect(req.data.createSingleGroupProtected).toEqual(null); + expect(req.errors.length).toEqual(1); + expect((req.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test listPWProtecteds when the user is authorized.`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createPWProtected(input: { content: "Foobie", participants: "${PARTICIPANT_GROUP_NAME}", watchers: "${WATCHER_GROUP_NAME}" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + + expect(req.data.createPWProtected).toBeTruthy(); + + const uReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + updatePWProtected(input: { id: "${req.data.createPWProtected.id}", content: "Foobie2" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + expect(uReq.data.updatePWProtected).toBeTruthy(); + + const req2 = await GRAPHQL_CLIENT_1.query( + ` + query { + listPWProtecteds { + items { + id + content + participants + watchers + } + nextToken + } + } + `, + {}, + ); + expect(req2.data.listPWProtecteds.items.length).toEqual(1); + expect(req2.data.listPWProtecteds.items[0].id).toEqual(req.data.createPWProtected.id); + expect(req2.data.listPWProtecteds.items[0].content).toEqual('Foobie2'); + + const req3 = await GRAPHQL_CLIENT_1.query( + ` + query { + getPWProtected(id: "${req.data.createPWProtected.id}") { + id + content + participants + watchers + } + } + `, + {}, + ); + + expect(req3.data.getPWProtected).toBeTruthy(); + + const dReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deletePWProtected(input: { id: "${req.data.createPWProtected.id}" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + expect(dReq.data.deletePWProtected).toBeTruthy(); + }); + + test(`Test listPWProtecteds when groups is null in dynamodb.`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + `mutation { + createPWProtected(input: { content: "Foobie" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + + expect(req.data.createPWProtected).toBeTruthy(); + + const req2 = await GRAPHQL_CLIENT_1.query( + ` + query { + listPWProtecteds { + items { + id + content + participants + watchers + } + nextToken + } + } + `, + {}, + ); + expect(req2.data.listPWProtecteds.items.length).toEqual(0); + + const req3 = await GRAPHQL_CLIENT_1.query( + ` + query { + getPWProtected(id: "${req.data.createPWProtected.id}") { + id + content + participants + watchers + } + } + `, + {}, + ); + + expect(req3.data.getPWProtected).toEqual(null); + expect(req3.errors.length).toEqual(1); + expect((req3.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + test(`Test Protecteds when the user is not authorized.`, async () => { + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createPWProtected(input: { content: "Barbie", participants: "${PARTICIPANT_GROUP_NAME}", watchers: "${WATCHER_GROUP_NAME}" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + + expect(req.data.createPWProtected).toBeTruthy(); + + const req2 = await GRAPHQL_CLIENT_3.query( + ` + query { + listPWProtecteds { + items { + id + content + participants + watchers + } + nextToken + } + } + `, + {}, + ); + + expect(req2.data.listPWProtecteds.items.length).toEqual(0); + expect(req2.data.listPWProtecteds.nextToken).toBeNull(); + + const uReq = await GRAPHQL_CLIENT_3.query( + ` + mutation { + updatePWProtected(input: { id: "${req.data.createPWProtected.id}", content: "Foobie2" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + expect(uReq.data.updatePWProtected).toBeNull(); + + const req3 = await GRAPHQL_CLIENT_3.query( + ` + query { + getPWProtected(id: "${req.data.createPWProtected.id}") { + id + content + participants + watchers + } + } + `, + {}, + ); + + expect(req3.data.getPWProtected).toBeNull(); + + const dReq = await GRAPHQL_CLIENT_3.query( + ` + mutation { + deletePWProtected(input: { id: "${req.data.createPWProtected.id}" }) { + id + content + participants + watchers + } + } + `, + {}, + ); + expect(dReq.data.deletePWProtected).toBeNull(); + + // The record should still exist after delete. + const getReq = await GRAPHQL_CLIENT_1.query( + ` + query { + getPWProtected(id: "${req.data.createPWProtected.id}") { + id + content + participants + watchers + } + } + `, + {}, + ); + expect(getReq.data.getPWProtected).toBeTruthy(); + }); + + test(`Test creating, updating, and deleting an admin note as an admin`, async () => { + const req = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAdminNote(input: { content: "Hello" }) { + id + content + } + } + `, + {}, + ); + + expect(req.data.createAdminNote.id).toBeDefined(); + expect(req.data.createAdminNote.content).toEqual('Hello'); + const req2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + updateAdminNote(input: { id: "${req.data.createAdminNote.id}", content: "Hello 2" }) { + id + content + } + } + `, + {}, + ); + + expect(req2.data.updateAdminNote.id).toEqual(req.data.createAdminNote.id); + expect(req2.data.updateAdminNote.content).toEqual('Hello 2'); + const req3 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAdminNote(input: { id: "${req.data.createAdminNote.id}" }) { + id + content + } + } + `, + {}, + ); + + expect(req3.data.deleteAdminNote.id).toEqual(req.data.createAdminNote.id); + expect(req3.data.deleteAdminNote.content).toEqual('Hello 2'); + }); + + test(`Test creating, updating, and deleting an admin note as a non admin`, async () => { + const adminReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAdminNote(input: { content: "Hello" }) { + id + content + } + } + `, + {}, + ); + expect(adminReq.data.createAdminNote.id).toBeDefined(); + expect(adminReq.data.createAdminNote.content).toEqual('Hello'); + + const req = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAdminNote(input: { content: "Hello" }) { + id + content + } + } + `, + {}, + ); + + expect(req.errors.length).toEqual(1); + expect((req.errors[0] as any).errorType).toEqual('Unauthorized'); + + const req2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateAdminNote(input: { id: "${adminReq.data.createAdminNote.id}", content: "Hello 2" }) { + id + content + } + } + `, + {}, + ); + + expect(req2.errors.length).toEqual(1); + expect((req2.errors[0] as any).errorType).toEqual('Unauthorized'); + + const req3 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + deleteAdminNote(input: { id: "${adminReq.data.createAdminNote.id}" }) { + id + content + } + } + `, + {}, + ); + + expect(req3.errors.length).toEqual(1); + expect((req3.errors[0] as any).errorType).toEqual('Unauthorized'); + }); + + /** + * Get Query Tests + */ + + test(`Test getAllThree as admin.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + + const fetchOwnedBy2AsAdmin = await GRAPHQL_CLIENT_1.query( + ` + query { + getAllThree(id: "${ownedBy2.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedBy2AsAdmin.data.getAllThree).toBeTruthy(); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test getAllThree as owner.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + + const fetchOwnedBy2AsOwner = await GRAPHQL_CLIENT_2.query( + ` + query { + getAllThree(id: "${ownedBy2.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedBy2AsOwner.data.getAllThree).toBeTruthy(); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test getAllThree as one of a set of editors.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + editors: ["user2@test.com"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + + const fetchOwnedBy2AsEditor = await GRAPHQL_CLIENT_2.query( + ` + query { + getAllThree(id: "${ownedBy2.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedBy2AsEditor.data.getAllThree).toBeTruthy(); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test getAllThree as a member of a dynamic group.`, async () => { + const ownedByAdmins = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + groups: ["Devs"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdmins.data.createAllThree).toBeTruthy(); + + const fetchOwnedByAdminsAsAdmin = await GRAPHQL_CLIENT_2.query( + ` + query { + getAllThree(id: "${ownedByAdmins.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsAdmin.data.getAllThree).toBeTruthy(); + + const fetchOwnedByAdminsAsNonAdmin = await GRAPHQL_CLIENT_3.query( + ` + query { + getAllThree(id: "${ownedByAdmins.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsNonAdmin.errors.length).toEqual(1); + expect((fetchOwnedByAdminsAsNonAdmin.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByAdmins.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByAdmins.data.createAllThree.id); + }); + + test(`Test getAllThree as a member of the alternative group.`, async () => { + const ownedByAdmins = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdmins.data.createAllThree).toBeTruthy(); + + const fetchOwnedByAdminsAsAdmin = await GRAPHQL_CLIENT_2.query( + ` + query { + getAllThree(id: "${ownedByAdmins.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsAdmin.data.getAllThree).toBeTruthy(); + + const fetchOwnedByAdminsAsNonAdmin = await GRAPHQL_CLIENT_3.query( + ` + query { + getAllThree(id: "${ownedByAdmins.data.createAllThree.id}") { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsNonAdmin.errors.length).toEqual(1); + expect((fetchOwnedByAdminsAsNonAdmin.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByAdmins.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByAdmins.data.createAllThree.id); + }); + + /** + * List Query Tests + */ + + test(`Test listAllThrees as admin.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + + const fetchOwnedBy2AsAdmin = await GRAPHQL_CLIENT_1.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedBy2AsAdmin.data.listAllThrees.items).toHaveLength(1); + expect(fetchOwnedBy2AsAdmin.data.listAllThrees.items[0].id).toEqual(ownedBy2.data.createAllThree.id); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test listAllThrees as owner.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + + const fetchOwnedBy2AsOwner = await GRAPHQL_CLIENT_2.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedBy2AsOwner.data.listAllThrees.items).toHaveLength(1); + expect(fetchOwnedBy2AsOwner.data.listAllThrees.items[0].id).toEqual(ownedBy2.data.createAllThree.id); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test listAllThrees as one of a set of editors.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + editors: ["user2@test.com"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + + const fetchOwnedBy2AsEditor = await GRAPHQL_CLIENT_2.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedBy2AsEditor.data.listAllThrees.items).toHaveLength(1); + expect(fetchOwnedBy2AsEditor.data.listAllThrees.items[0].id).toEqual(ownedBy2.data.createAllThree.id); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test listAllThrees as a member of a dynamic group.`, async () => { + const ownedByAdmins = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + groups: ["Devs"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdmins.data.createAllThree).toBeTruthy(); + + const fetchOwnedByAdminsAsAdmin = await GRAPHQL_CLIENT_2.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsAdmin.data.listAllThrees.items).toHaveLength(1); + expect(fetchOwnedByAdminsAsAdmin.data.listAllThrees.items[0].id).toEqual(ownedByAdmins.data.createAllThree.id); + + const fetchOwnedByAdminsAsNonAdmin = await GRAPHQL_CLIENT_3.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsNonAdmin.data.listAllThrees.items).toHaveLength(0); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByAdmins.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByAdmins.data.createAllThree.id); + }); + + test(`Test getAllThree as a member of the alternative group.`, async () => { + const ownedByAdmins = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdmins.data.createAllThree).toBeTruthy(); + + const fetchOwnedByAdminsAsAdmin = await GRAPHQL_CLIENT_2.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsAdmin.data.listAllThrees.items).toHaveLength(1); + expect(fetchOwnedByAdminsAsAdmin.data.listAllThrees.items[0].id).toEqual(ownedByAdmins.data.createAllThree.id); + + const fetchOwnedByAdminsAsNonAdmin = await GRAPHQL_CLIENT_3.query( + ` + query { + listAllThrees { + items { + id + owner + editors + groups + alternativeGroup + } + } + } + `, + {}, + ); + expect(fetchOwnedByAdminsAsNonAdmin.data.listAllThrees.items).toHaveLength(0); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByAdmins.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByAdmins.data.createAllThree.id); + }); + + /** + * Create Mutation Tests + */ + + test(`Test createAllThree as admin.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + // set by input + expect(ownedBy2.data.createAllThree.owner).toEqual('user2@test.com'); + // not auto filled as the admin condition was met + expect(ownedBy2.data.createAllThree.editors).toBeNull(); + expect(ownedBy2.data.createAllThree.groups).toBeNull(); + expect(ownedBy2.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedBy2NoEditors = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com", + editors: [] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2NoEditors.data.createAllThree).toBeTruthy(); + // set by input + expect(ownedBy2NoEditors.data.createAllThree.owner).toEqual('user2@test.com'); + // set by input + expect(ownedBy2NoEditors.data.createAllThree.editors).toHaveLength(0); + expect(ownedBy2NoEditors.data.createAllThree.groups).toBeNull(); + expect(ownedBy2NoEditors.data.createAllThree.alternativeGroup).toBeNull(); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + + const deleteReq2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2NoEditors.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq2.data.deleteAllThree.id).toEqual(ownedBy2NoEditors.data.createAllThree.id); + }); + + test(`Test createAllThree as owner.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com", + editors: [] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + expect(ownedBy2.data.createAllThree.owner).toEqual('user2@test.com'); + expect(ownedBy2.data.createAllThree.editors).toHaveLength(0); + expect(ownedBy2.data.createAllThree.groups).toBeNull(); + expect(ownedBy2.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedBy1 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: "user1@test.com", + editors: [] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy1.errors.length).toEqual(1); + expect((ownedBy1.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test createAllThree as one of a set of editors.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: ["user2@test.com"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + expect(ownedBy2.data.createAllThree.owner).toBeNull(); + expect(ownedBy2.data.createAllThree.editors[0]).toEqual('user2@test.com'); + expect(ownedBy2.data.createAllThree.groups).toBeNull(); + expect(ownedBy2.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedBy2WithDefaultOwner = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + editors: ["user2@test.com"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2WithDefaultOwner.data.createAllThree).toBeTruthy(); + expect(ownedBy2WithDefaultOwner.data.createAllThree.owner).toEqual('user2@test.com'); + expect(ownedBy2WithDefaultOwner.data.createAllThree.editors[0]).toEqual('user2@test.com'); + expect(ownedBy2WithDefaultOwner.data.createAllThree.groups).toBeNull(); + expect(ownedBy2WithDefaultOwner.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedByEditorsUnauthed = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: ["user1@test.com"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByEditorsUnauthed.errors.length).toEqual(1); + expect((ownedByEditorsUnauthed.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + + const deleteReq2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2WithDefaultOwner.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq2.data.deleteAllThree.id).toEqual(ownedBy2WithDefaultOwner.data.createAllThree.id); + }); + + test(`Test createAllThree as a member of a dynamic group.`, async () => { + const ownedByDevs = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + groups: ["Devs"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByDevs.data.createAllThree).toBeTruthy(); + expect(ownedByDevs.data.createAllThree.owner).toBeNull(); + expect(ownedByDevs.data.createAllThree.editors).toHaveLength(0); + expect(ownedByDevs.data.createAllThree.groups[0]).toEqual('Devs'); + expect(ownedByDevs.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedByAdminsUnauthed = await GRAPHQL_CLIENT_3.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + groups: ["Devs"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdminsUnauthed.errors.length).toEqual(1); + expect((ownedByAdminsUnauthed.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByDevs.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByDevs.data.createAllThree.id); + }); + + test(`Test createAllThree as a member of the alternative group.`, async () => { + const ownedByAdmins = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdmins.data.createAllThree).toBeTruthy(); + expect(ownedByAdmins.data.createAllThree.owner).toBeNull(); + expect(ownedByAdmins.data.createAllThree.editors).toHaveLength(0); + expect(ownedByAdmins.data.createAllThree.alternativeGroup).toEqual('Devs'); + + const ownedByAdminsUnauthed = await GRAPHQL_CLIENT_3.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + alternativeGroup: "Admin" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdminsUnauthed.errors.length).toEqual(1); + expect((ownedByAdminsUnauthed.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByAdmins.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByAdmins.data.createAllThree.id); + }); + + /** + * Update Mutation Tests + */ + + test(`Test updateAllThree and deleteAllThree as admin.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + editors: [] + owner: "user2@test.com" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + // set by input + expect(ownedBy2.data.createAllThree.owner).toEqual('user2@test.com'); + // auto filled as logged in user. + expect(ownedBy2.data.createAllThree.editors).toHaveLength(0); + expect(ownedBy2.data.createAllThree.groups).toBeNull(); + expect(ownedBy2.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedByTwoUpdate = await GRAPHQL_CLIENT_1.query( + ` + mutation { + updateAllThree(input: { + id: "${ownedBy2.data.createAllThree.id}", + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByTwoUpdate.data.updateAllThree).toBeTruthy(); + // set by input + expect(ownedByTwoUpdate.data.updateAllThree.owner).toEqual('user2@test.com'); + // set by input + expect(ownedByTwoUpdate.data.updateAllThree.editors).toHaveLength(0); + expect(ownedByTwoUpdate.data.updateAllThree.groups).toBeNull(); + expect(ownedByTwoUpdate.data.updateAllThree.alternativeGroup).toEqual('Devs'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test updateAllThree and deleteAllThree as owner.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: "user2@test.com", + editors: [] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + expect(ownedBy2.data.createAllThree.owner).toEqual('user2@test.com'); + expect(ownedBy2.data.createAllThree.editors).toHaveLength(0); + expect(ownedBy2.data.createAllThree.groups).toBeNull(); + expect(ownedBy2.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedBy2Update = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateAllThree(input: { + id: "${ownedBy2.data.createAllThree.id}", + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2Update.data.updateAllThree).toBeTruthy(); + // set by input + expect(ownedBy2Update.data.updateAllThree.owner).toEqual('user2@test.com'); + // set by input + expect(ownedBy2Update.data.updateAllThree.editors).toHaveLength(0); + expect(ownedBy2Update.data.updateAllThree.groups).toBeNull(); + expect(ownedBy2Update.data.updateAllThree.alternativeGroup).toEqual('Devs'); + + const deleteReq = await GRAPHQL_CLIENT_2.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test updateAllThree and deleteAllThree as one of a set of editors.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: ["user2@test.com"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedBy2.data.createAllThree).toBeTruthy(); + expect(ownedBy2.data.createAllThree.owner).toBeNull(); + expect(ownedBy2.data.createAllThree.editors[0]).toEqual('user2@test.com'); + expect(ownedBy2.data.createAllThree.groups).toBeNull(); + expect(ownedBy2.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedByUpdate = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateAllThree(input: { + id: "${ownedBy2.data.createAllThree.id}", + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByUpdate.data.updateAllThree).toBeTruthy(); + // set by input + expect(ownedByUpdate.data.updateAllThree.owner).toBeNull(); + // set by input + expect(ownedByUpdate.data.updateAllThree.editors[0]).toEqual('user2@test.com'); + expect(ownedByUpdate.data.updateAllThree.groups).toBeNull(); + expect(ownedByUpdate.data.updateAllThree.alternativeGroup).toEqual('Devs'); + + const deleteReq = await GRAPHQL_CLIENT_2.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedBy2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedBy2.data.createAllThree.id); + }); + + test(`Test updateAllThree and deleteAllThree as a member of a dynamic group.`, async () => { + const ownedByDevs = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + groups: ["Devs"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByDevs.data.createAllThree).toBeTruthy(); + expect(ownedByDevs.data.createAllThree.owner).toBeNull(); + expect(ownedByDevs.data.createAllThree.editors).toHaveLength(0); + expect(ownedByDevs.data.createAllThree.groups[0]).toEqual('Devs'); + expect(ownedByDevs.data.createAllThree.alternativeGroup).toBeNull(); + + const ownedByUpdate = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateAllThree(input: { + id: "${ownedByDevs.data.createAllThree.id}", + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByUpdate.data.updateAllThree).toBeTruthy(); + // set by input + expect(ownedByUpdate.data.updateAllThree.owner).toBeNull(); + // set by input + expect(ownedByUpdate.data.updateAllThree.editors).toHaveLength(0); + expect(ownedByUpdate.data.updateAllThree.groups[0]).toEqual('Devs'); + expect(ownedByUpdate.data.updateAllThree.alternativeGroup).toEqual('Devs'); + + const ownedByAdminsUnauthed = await GRAPHQL_CLIENT_3.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + groups: ["Devs"] + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdminsUnauthed.errors.length).toEqual(1); + expect((ownedByAdminsUnauthed.errors[0] as any).errorType).toEqual('Unauthorized'); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByDevs.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByDevs.data.createAllThree.id); + }); + + test(`Test updateAllThree and deleteAllThree as a member of the alternative group.`, async () => { + const ownedByDevs = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByDevs.data.createAllThree).toBeTruthy(); + expect(ownedByDevs.data.createAllThree.owner).toBeNull(); + expect(ownedByDevs.data.createAllThree.editors).toHaveLength(0); + expect(ownedByDevs.data.createAllThree.alternativeGroup).toEqual('Devs'); + + const ownedByUpdate = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateAllThree(input: { + id: "${ownedByDevs.data.createAllThree.id}", + alternativeGroup: "Admin" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByUpdate.data.updateAllThree).toBeTruthy(); + // set by input + expect(ownedByUpdate.data.updateAllThree.owner).toBeNull(); + // set by input + expect(ownedByUpdate.data.updateAllThree.editors).toHaveLength(0); + expect(ownedByUpdate.data.updateAllThree.groups).toBeNull(); + expect(ownedByUpdate.data.updateAllThree.alternativeGroup).toEqual('Admin'); + + const ownedByAdminsUnauthed = await GRAPHQL_CLIENT_2.query( + ` + mutation { + updateAllThree(input: { + id: "${ownedByDevs.data.createAllThree.id}", + alternativeGroup: "Dev" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByAdminsUnauthed.errors.length).toEqual(1); + expect((ownedByAdminsUnauthed.errors[0] as any).data).toBeNull(); + expect((ownedByAdminsUnauthed.errors[0] as any).errorType).toEqual('Unauthorized'); + + const ownedByDevs2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createAllThree(input: { + owner: null, + editors: [], + alternativeGroup: "Devs" + }) { + id + owner + editors + groups + alternativeGroup + } + } + `, + {}, + ); + expect(ownedByDevs2.data.createAllThree).toBeTruthy(); + expect(ownedByDevs2.data.createAllThree.owner).toBeNull(); + expect(ownedByDevs2.data.createAllThree.editors).toHaveLength(0); + expect(ownedByDevs2.data.createAllThree.alternativeGroup).toEqual('Devs'); + + const deleteReq2 = await GRAPHQL_CLIENT_2.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByDevs2.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq2.data.deleteAllThree.id).toEqual(ownedByDevs2.data.createAllThree.id); + + const deleteReq = await GRAPHQL_CLIENT_1.query( + ` + mutation { + deleteAllThree(input: { id: "${ownedByDevs.data.createAllThree.id}" }) { + id + } + } + `, + {}, + ); + expect(deleteReq.data.deleteAllThree.id).toEqual(ownedByDevs.data.createAllThree.id); + }); + + test(`Test createTestIdentity as admin.`, async () => { + const ownedBy2 = await GRAPHQL_CLIENT_1.query( + ` + mutation { + createTestIdentity(input: { + title: "Test title" + }) { + id + title + owner + } + } + `, + {}, + ); + expect(ownedBy2.data.createTestIdentity).toBeTruthy(); + expect(ownedBy2.data.createTestIdentity.title).toEqual('Test title'); + expect(ownedBy2.data.createTestIdentity.owner.slice(0, 19)).toEqual('https://cognito-idp'); + + // user 2 should be able to update because they share the same issuer. + const update = await GRAPHQL_CLIENT_3.query( + ` + mutation { + updateTestIdentity(input: { + id: "${ownedBy2.data.createTestIdentity.id}", + title: "Test title update" + }) { + id + title + owner + } + } + `, + {}, + ); + expect(update.data.updateTestIdentity).toBeTruthy(); + expect(update.data.updateTestIdentity.title).toEqual('Test title update'); + expect(update.data.updateTestIdentity.owner.slice(0, 19)).toEqual('https://cognito-idp'); + + // user 2 should be able to get because they share the same issuer. + const getReq = await GRAPHQL_CLIENT_3.query( + ` + query { + getTestIdentity(id: "${ownedBy2.data.createTestIdentity.id}") { + id + title + owner + } + } + `, + {}, + ); + expect(getReq.data.getTestIdentity).toBeTruthy(); + expect(getReq.data.getTestIdentity.title).toEqual('Test title update'); + expect(getReq.data.getTestIdentity.owner.slice(0, 19)).toEqual('https://cognito-idp'); + + const listResponse = await GRAPHQL_CLIENT_3.query( + `query { + listTestIdentities(filter: { title: { eq: "Test title update" } }, limit: 100) { + items { + id + title + owner + } + } + }`, + {}, + ); + const relevantPost = listResponse.data.listTestIdentities.items.find(p => p.id === getReq.data.getTestIdentity.id); + expect(relevantPost).toBeTruthy(); + expect(relevantPost.title).toEqual('Test title update'); + expect(relevantPost.owner.slice(0, 19)).toEqual('https://cognito-idp'); + + // user 2 should be able to delete because they share the same issuer. + const delReq = await GRAPHQL_CLIENT_3.query( + ` + mutation { + deleteTestIdentity(input: { + id: "${ownedBy2.data.createTestIdentity.id}" + }) { + id + title + owner + } + } + `, + {}, + ); + expect(delReq.data.deleteTestIdentity).toBeTruthy(); + expect(delReq.data.deleteTestIdentity.title).toEqual('Test title update'); + expect(delReq.data.deleteTestIdentity.owner.slice(0, 19)).toEqual('https://cognito-idp'); + }); + + /** + * Test 'operations' argument + */ + test("Test get and list with 'read' operation set", async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createNoOwner: createOwnerReadProtected(input: { id: "1", sk: "1", content: "Hello, World! - No Owner" }) { + id + content + owner + } + createOwnerReadProtected(input: { id: "1", sk: "2", content: "Hello, World!", owner: "${USERNAME2}" }) { + id + content + owner + } + createNoOwner2: createOwnerReadProtected(input: { id: "1", sk: "3", content: "Hello, World! - No Owner 2" }) { + id + content + owner + } + }`, + {}, + ); + + expect(response.data.createOwnerReadProtected.id).toBeDefined(); + expect(response.data.createOwnerReadProtected.content).toEqual('Hello, World!'); + expect(response.data.createOwnerReadProtected.owner).toEqual(USERNAME2); + + const response2 = await GRAPHQL_CLIENT_3.query( + `query { + getOwnerReadProtected(id: "${response.data.createOwnerReadProtected.id}", sk:"2") { + id content owner + } + }`, + {}, + ); + expect(response2.data.getOwnerReadProtected).toBeNull(); + expect(response2.errors).toHaveLength(1); + + const response3 = await GRAPHQL_CLIENT_2.query( + `query { + getOwnerReadProtected(id: "${response.data.createOwnerReadProtected.id}", sk:"2") { + id content owner + } + }`, + {}, + ); + expect(response3.data.getOwnerReadProtected.id).toBeDefined(); + expect(response3.data.getOwnerReadProtected.content).toEqual('Hello, World!'); + expect(response3.data.getOwnerReadProtected.owner).toEqual(USERNAME2); + + const response4 = await GRAPHQL_CLIENT_2.query( + `query { + listOwnerReadProtecteds(id: "1") { + items { + id content owner + } + } + }`, + {}, + ); + expect(response4.data.listOwnerReadProtecteds.items.length).toEqual(1); + + const response5 = await GRAPHQL_CLIENT_3.query( + `query { + listOwnerReadProtecteds { + items { + id content owner + } + } + }`, + {}, + ); + expect(response5.data.listOwnerReadProtecteds.items).toHaveLength(0); + }); + + test("Test createOwnerCreateUpdateDeleteProtected with 'create' operation set", async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createOwnerCreateUpdateDeleteProtected(input: { content: "Hello, World!", owner: "${USERNAME1}" }) { + id + content + owner + } + }`, + {}, + ); + expect(response.data.createOwnerCreateUpdateDeleteProtected.id).toBeDefined(); + expect(response.data.createOwnerCreateUpdateDeleteProtected.content).toEqual('Hello, World!'); + expect(response.data.createOwnerCreateUpdateDeleteProtected.owner).toEqual(USERNAME1); + + const response2 = await GRAPHQL_CLIENT_1.query( + `mutation { + createOwnerCreateUpdateDeleteProtected(input: { content: "Hello, World!", owner: "${USERNAME2}" }) { + id + content + owner + } + }`, + {}, + ); + expect(response2.data.createOwnerCreateUpdateDeleteProtected).toBeNull(); + expect(response2.errors).toHaveLength(1); + }); + + test("Test updateOwnerCreateUpdateDeleteProtected with 'update' operation set", async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createOwnerCreateUpdateDeleteProtected(input: { content: "Hello, World!", owner: "${USERNAME1}" }) { + id + content + owner + } + }`, + {}, + ); + expect(response.data.createOwnerCreateUpdateDeleteProtected.id).toBeDefined(); + expect(response.data.createOwnerCreateUpdateDeleteProtected.content).toEqual('Hello, World!'); + expect(response.data.createOwnerCreateUpdateDeleteProtected.owner).toEqual(USERNAME1); + + const response2 = await GRAPHQL_CLIENT_2.query( + `mutation { + updateOwnerCreateUpdateDeleteProtected( + input: { + id: "${response.data.createOwnerCreateUpdateDeleteProtected.id}", + content: "Bye, World!" + } + ) { + id + content + owner + } + }`, + {}, + ); + expect(response2.data.updateOwnerCreateUpdateDeleteProtected).toBeNull(); + expect(response2.errors).toHaveLength(1); + + const response3 = await GRAPHQL_CLIENT_1.query( + `mutation { + updateOwnerCreateUpdateDeleteProtected( + input: { + id: "${response.data.createOwnerCreateUpdateDeleteProtected.id}", + content: "Bye, World!" + } + ) { + id + content + owner + } + }`, + {}, + ); + expect(response3.data.updateOwnerCreateUpdateDeleteProtected.id).toBeDefined(); + expect(response3.data.updateOwnerCreateUpdateDeleteProtected.content).toEqual('Bye, World!'); + expect(response3.data.updateOwnerCreateUpdateDeleteProtected.owner).toEqual(USERNAME1); + }); + + test("Test deleteOwnerCreateUpdateDeleteProtected with 'update' operation set", async () => { + const response = await GRAPHQL_CLIENT_1.query( + `mutation { + createOwnerCreateUpdateDeleteProtected(input: { content: "Hello, World!", owner: "${USERNAME1}" }) { + id + content + owner + } + }`, + {}, + ); + expect(response.data.createOwnerCreateUpdateDeleteProtected.id).toBeDefined(); + expect(response.data.createOwnerCreateUpdateDeleteProtected.content).toEqual('Hello, World!'); + expect(response.data.createOwnerCreateUpdateDeleteProtected.owner).toEqual(USERNAME1); + + const response2 = await GRAPHQL_CLIENT_2.query( + `mutation { + deleteOwnerCreateUpdateDeleteProtected( + input: { + id: "${response.data.createOwnerCreateUpdateDeleteProtected.id}" + } + ) { + id + content + owner + } + }`, + {}, + ); + expect(response2.data.deleteOwnerCreateUpdateDeleteProtected).toBeNull(); + expect(response2.errors).toHaveLength(1); + + const response3 = await GRAPHQL_CLIENT_1.query( + `mutation { + deleteOwnerCreateUpdateDeleteProtected( + input: { + id: "${response.data.createOwnerCreateUpdateDeleteProtected.id}" + } + ) { + id + content + owner + } + }`, + {}, + ); + expect(response3.data.deleteOwnerCreateUpdateDeleteProtected.id).toBeDefined(); + expect(response3.data.deleteOwnerCreateUpdateDeleteProtected.content).toEqual('Hello, World!'); + expect(response3.data.deleteOwnerCreateUpdateDeleteProtected.owner).toEqual(USERNAME1); + }); + + test('Test allow private combined with groups as Admin and non-admin users', async () => { + const create = `mutation { + p1: createPerformance(input: { + id: "P1" + performer: "Perf #1" + description: "Description" + time: "2019-11-11T00:00:00Z" + performanceStageId: "S1" + }) { + id + } + + p2: createPerformance(input: { + id: "P2" + performer: "Perf #2" + description: "Description" + time: "2019-11-11T00:00:00Z" + performanceStageId: "S1" + }) { + id + } + + s1: createStage(input: { + id: "S1" + name: "Stage #1" + }) { + id + } + } + `; + + // Create as non-admin user, should fail + const response1 = await GRAPHQL_CLIENT_3.query(create, {}); + + expect(response1.data.p1).toBeNull(); + expect(response1.data.p2).toBeNull(); + expect(response1.data.s1).toBeNull(); + expect(response1.errors.length).toEqual(3); + expect((response1.errors[0] as any).errorType).toEqual('Unauthorized'); + expect((response1.errors[1] as any).errorType).toEqual('Unauthorized'); + expect((response1.errors[2] as any).errorType).toEqual('Unauthorized'); + + // Create as Admin, should succeed + const response2 = await GRAPHQL_CLIENT_1.query(create, {}); + + expect(response2.data.p1.id).toEqual('P1'); + expect(response2.data.p2.id).toEqual('P2'); + expect(response2.data.s1.id).toEqual('S1'); + + const update = `mutation { + updatePerformance(input: { + id: "P1" + performer: "Best Perf #1" + }) { + id + performer + } + } + `; + + // Update as non-admin user, should fail + const response3 = await GRAPHQL_CLIENT_3.query(update, {}); + + expect(response3.data.updatePerformance).toBeNull(); + expect(response3.errors.length).toEqual(1); + expect((response3.errors[0] as any).errorType).toEqual('Unauthorized'); + + // Update as Admin, should succeed + const response4 = await GRAPHQL_CLIENT_1.query(update, {}); + + expect(response4.data.updatePerformance.id).toEqual('P1'); + expect(response4.data.updatePerformance.performer).toEqual('Best Perf #1'); + + // List as non-admin and Admin user as well, should succeed + const list = `query List { + listPerformances { + items { + id + performer + description + time + stage { + name + } + } + } + } + `; + + const response5 = await GRAPHQL_CLIENT_3.query(list, {}); + + expect(response5.data.listPerformances).toBeDefined(); + expect(response5.data.listPerformances.items).toBeDefined(); + expect(response5.data.listPerformances.items.length).toEqual(2); + expect(response5.data.listPerformances.items[0].id).toEqual('P2'); + expect(response5.data.listPerformances.items[0].performer).toEqual('Perf #2'); + expect(response5.data.listPerformances.items[0].stage).toBeDefined(); + expect(response5.data.listPerformances.items[0].stage.name).toEqual('Stage #1'); + expect(response5.data.listPerformances.items[1].id).toEqual('P1'); + expect(response5.data.listPerformances.items[1].performer).toEqual('Best Perf #1'); + expect(response5.data.listPerformances.items[1].stage).toBeDefined(); + expect(response5.data.listPerformances.items[1].stage.name).toEqual('Stage #1'); + + const response6 = await GRAPHQL_CLIENT_1.query(list, {}); + + expect(response6.data.listPerformances).toBeDefined(); + expect(response6.data.listPerformances.items).toBeDefined(); + expect(response6.data.listPerformances.items.length).toEqual(2); + expect(response6.data.listPerformances.items[0].id).toEqual('P2'); + expect(response6.data.listPerformances.items[0].performer).toEqual('Perf #2'); + expect(response6.data.listPerformances.items[0].stage).toBeDefined(); + expect(response6.data.listPerformances.items[0].stage.name).toEqual('Stage #1'); + expect(response6.data.listPerformances.items[1].id).toEqual('P1'); + expect(response6.data.listPerformances.items[1].performer).toEqual('Best Perf #1'); + expect(response6.data.listPerformances.items[1].stage.name).toEqual('Stage #1'); + expect(response6.data.listPerformances.items[1].stage).toBeDefined(); + + // Get as non-admin and Admin user as well, should succeed + const get = `query Get { + getPerformance(id: "P1") { + id + performer + description + time + stage { + name + } + } + } + `; + + const response7 = await GRAPHQL_CLIENT_3.query(get, {}); + + expect(response7.data.getPerformance).toBeDefined(); + expect(response7.data.getPerformance.id).toEqual('P1'); + expect(response7.data.getPerformance.performer).toEqual('Best Perf #1'); + expect(response7.data.getPerformance.stage).toBeDefined(); + expect(response7.data.getPerformance.stage.name).toEqual('Stage #1'); + + const response8 = await GRAPHQL_CLIENT_1.query(get, {}); + + expect(response8.data.getPerformance).toBeDefined(); + expect(response8.data.getPerformance.id).toEqual('P1'); + expect(response8.data.getPerformance.performer).toEqual('Best Perf #1'); + expect(response8.data.getPerformance.stage).toBeDefined(); + expect(response8.data.getPerformance.stage.name).toEqual('Stage #1'); + + const deleteMutation = `mutation { + deletePerformance(input: { + id: "P1" + }) { + id + } + } + `; + + // Delete as non-admin user, should fail + const response9 = await GRAPHQL_CLIENT_3.query(deleteMutation, {}); + + expect(response9.data.deletePerformance).toBeNull(); + expect(response9.errors.length).toEqual(1); + expect((response9.errors[0] as any).errorType).toEqual('Unauthorized'); + + // Delete as Admin, should succeed + const response10 = await GRAPHQL_CLIENT_1.query(deleteMutation, {}); + + expect(response10.data.deletePerformance).toBeDefined(); + expect(response10.data.deletePerformance.id).toEqual('P1'); + }); + + test('Test authorized user can get Performance with no created stage', async () => { + const createPerf = `mutation { + create: createPerformance(input: { + id: "P3" + performer: "Perf #3" + description: "desc" + time: "2021-11-11T00:00:00Z" + }) { + id + performer + description + time + createdAt + updatedAt + } + }`; + + const getPerf = `query { + g1: getPerformance(id: "P3") { + id + performer + description + time + stage { + id + name + createdAt + updatedAt + } + createdAt + updatedAt + } + } + `; + + const createStage = `mutation { + createStage(input: {name: "stage3", id: "003"}) { + createdAt + id + name + updatedAt + } + }`; + + const updatePerf = `mutation { + u1: updatePerformance(input: {id: "P3", performanceStageId: "003"}) { + createdAt + description + id + performer + time + updatedAt + stage { + id + name + } + } + }`; + + // first make a query to the record 'P3' + const response1 = await GRAPHQL_CLIENT_1.query(getPerf, {}); + expect(response1).toBeDefined(); + expect(response1.data.g1).toBeNull(); + + // create performance and expect stage to be null + await GRAPHQL_CLIENT_1.query(createPerf, {}); + const response2 = await GRAPHQL_CLIENT_1.query(getPerf, {}); + expect(response2).toBeDefined(); + expect(response2.data.g1).toBeDefined(); + expect(response2.data.g1.id).toEqual('P3'); + expect(response2.data.g1.description).toEqual('desc'); + expect(response2.data.g1.stage).toBeNull(); + + //create stage and then add it to perf should show stage in perf + await GRAPHQL_CLIENT_1.query(createStage, {}); + const response3 = await GRAPHQL_CLIENT_1.query(updatePerf, {}); + expect(response3).toBeDefined(); + expect(response3.data.u1).toBeDefined(); + expect(response3.data.u1.id).toEqual('P3'); + expect(response3.data.u1.stage).toMatchObject({ + id: '003', + name: 'stage3', + }); + }); +}); diff --git a/packages/graphql-transformers-e2e-tests/src/deployNestedStacks.ts b/packages/graphql-transformers-e2e-tests/src/deployNestedStacks.ts index 2f13e615b2d..8970de56761 100644 --- a/packages/graphql-transformers-e2e-tests/src/deployNestedStacks.ts +++ b/packages/graphql-transformers-e2e-tests/src/deployNestedStacks.ts @@ -178,7 +178,7 @@ export async function deploy( } function addAPIKeys(stack: DeploymentResources) { - if (!stack.rootStack.Resources.GraphQLAPIKey) { + if (stack.rootStack.Parameters.CreateAPIKey && !stack.rootStack.Resources.GraphQLAPIKey) { stack.rootStack.Resources.GraphQLAPIKey = { Type: 'AWS::AppSync::ApiKey', Properties: { @@ -189,7 +189,7 @@ function addAPIKeys(stack: DeploymentResources) { }; } - if (!stack.rootStack.Outputs.GraphQLAPIKeyOutput) { + if (stack.rootStack.Parameters.CreateAPIKey && !stack.rootStack.Outputs.GraphQLAPIKeyOutput) { stack.rootStack.Outputs.GraphQLAPIKeyOutput = { Value: { 'Fn::GetAtt': ['GraphQLAPIKey', 'ApiKey'], diff --git a/packages/graphql-versioned-transformer/CHANGELOG.md b/packages/graphql-versioned-transformer/CHANGELOG.md index bcf254788de..01e9288cc6b 100644 --- a/packages/graphql-versioned-transformer/CHANGELOG.md +++ b/packages/graphql-versioned-transformer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.17.23-graphql-vnext-dev-preview.0](https://github.com/aws-amplify/amplify-cli/compare/graphql-versioned-transformer@4.17.22...graphql-versioned-transformer@4.17.23-graphql-vnext-dev-preview.0) (2021-09-27) + +**Note:** Version bump only for package graphql-versioned-transformer + + + + + ## [4.17.22](https://github.com/aws-amplify/amplify-cli/compare/graphql-versioned-transformer@4.17.21...graphql-versioned-transformer@4.17.22) (2021-09-18) **Note:** Version bump only for package graphql-versioned-transformer diff --git a/packages/graphql-versioned-transformer/package.json b/packages/graphql-versioned-transformer/package.json index b3da9f8af5b..62fb2ec87d0 100644 --- a/packages/graphql-versioned-transformer/package.json +++ b/packages/graphql-versioned-transformer/package.json @@ -1,6 +1,6 @@ { "name": "graphql-versioned-transformer", - "version": "4.17.22", + "version": "4.17.23-graphql-vnext-dev-preview.0", "description": "A GraphQL transform that enabled object versioning and conflict resolution for a @model type.", "repository": { "type": "git", @@ -23,13 +23,13 @@ }, "dependencies": { "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.3", - "graphql-transformer-common": "4.19.9", - "graphql-transformer-core": "6.29.7" + "graphql-mapping-template": "4.19.0-graphql-vnext-dev-preview.0", + "graphql-transformer-common": "4.19.10-graphql-vnext-dev-preview.0", + "graphql-transformer-core": "6.30.0-graphql-vnext-dev-preview.0" }, "devDependencies": { "@types/node": "^12.12.6", - "graphql-dynamodb-transformer": "6.22.22" + "graphql-dynamodb-transformer": "6.22.23-graphql-vnext-dev-preview.0" }, "jest": { "transform": { diff --git a/yarn.lock b/yarn.lock index f72c0ce8cbd..0db4a5d9db9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7509,9 +7509,9 @@ aws-sdk@^2.518.0, aws-sdk@^2.814.0, aws-sdk@^2.848.0, aws-sdk@^2.928.0, aws-sdk@ xml2js "0.4.19" aws-sdk@^2.979.0: - version "2.993.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.993.0.tgz#872c038b9cf78d35d6d90d99fd9b2973f99209ca" - integrity sha512-uAxPVkGM0+hWt+OmFUtNgQmmo3tQUW1MJD8wBWFpfw97QpG2WPMv6fEFBJmuaVt0LkElgTs+9oKJsu9WkPIC9Q== + version "2.992.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.992.0.tgz#3d8f663727cf3ea62a794a7499ae001fe6256abb" + integrity sha512-FP/AOu1nxfaPJ6to05eHriBUzvPiNapEwy96sm5GNOL8/T38k9//H6UhxLJ/46CzxFMH/Mo/WFp0qwpS39ev5A== dependencies: buffer "4.9.2" events "1.1.1"