diff --git a/codebuild_specs/e2e_workflow.yml b/codebuild_specs/e2e_workflow.yml index 1f15a19006..51453c0803 100644 --- a/codebuild_specs/e2e_workflow.yml +++ b/codebuild_specs/e2e_workflow.yml @@ -763,7 +763,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=14848' TEST_SUITE: >- src/__tests__/utils.test.ts|src/__tests__/ddb-iam-access.test.ts|src/__tests__/data-construct.test.ts|src/__tests__/custom-logic.test.ts|src/__tests__/amplify-table-5.test.ts - CLI_REGION: eu-south-1 + CLI_REGION: eu-west-1 depend-on: - publish_to_local_registry - identifier: >- @@ -775,7 +775,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=14848' TEST_SUITE: >- src/__tests__/add-resources.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/single-gsi-single-record.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/single-gsi-empty-table.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/single-gsi-1k-records.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/single-gsi-10k-records.test.ts - CLI_REGION: ap-northeast-2 + CLI_REGION: ap-south-1 depend-on: - publish_to_local_registry - identifier: >- @@ -787,7 +787,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=14848' TEST_SUITE: >- src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-update-attr-single-record.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-update-attr-empty-table.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-update-attr-1k-records.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-update-attr-10k-records.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-single-record.test.ts - CLI_REGION: ap-south-1 + CLI_REGION: ap-southeast-1 depend-on: - publish_to_local_registry - identifier: >- @@ -799,7 +799,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=14848' TEST_SUITE: >- src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-empty-table.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-1k-records.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/replace-2-gsis-10k-records.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/3-gsis-single-record.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/3-gsis-empty-table.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 depend-on: - publish_to_local_registry - identifier: 3_gsis_1k_records_3_gsis_10k_records @@ -810,7 +810,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=14848' TEST_SUITE: >- src/__tests__/deploy-velocity-temporarily-disabled/3-gsis-1k-records.test.ts|src/__tests__/deploy-velocity-temporarily-disabled/3-gsis-10k-records.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: ca-central-1 depend-on: - publish_to_local_registry - identifier: sql_pg_models @@ -834,6 +834,16 @@ batch: USE_PARENT_ACCOUNT: 1 depend-on: - publish_to_local_registry + - identifier: sql_pg_auto_increment + buildspec: codebuild_specs/run_cdk_tests.yml + env: + compute-type: BUILD_GENERAL1_MEDIUM + variables: + NODE_OPTIONS: '--max-old-space-size=6656' + TEST_SUITE: src/__tests__/sql-pg-auto-increment.test.ts + CLI_REGION: ap-southeast-2 + depend-on: + - publish_to_local_registry - identifier: sql_pg_array_objects buildspec: codebuild_specs/run_cdk_tests.yml env: @@ -841,7 +851,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/sql-pg-array-objects.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: ca-central-1 depend-on: - publish_to_local_registry - identifier: sql_mysql_canary @@ -851,7 +861,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/sql-mysql-canary.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: sql_models_2 @@ -861,7 +871,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/sql-models-2.test.ts - CLI_REGION: eu-central-1 + CLI_REGION: eu-north-1 depend-on: - publish_to_local_registry - identifier: sql_models_1 @@ -871,7 +881,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/sql-models-1.test.ts - CLI_REGION: eu-north-1 + CLI_REGION: eu-west-1 depend-on: - publish_to_local_registry - identifier: sql_iam_access @@ -881,7 +891,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/sql-iam-access.test.ts - CLI_REGION: eu-west-1 + CLI_REGION: eu-west-2 depend-on: - publish_to_local_registry - identifier: default_ddb_canary @@ -891,7 +901,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/default-ddb-canary.test.ts - CLI_REGION: eu-south-1 + CLI_REGION: eu-west-1 depend-on: - publish_to_local_registry - identifier: base_cdk_ap_east_1 @@ -1091,7 +1101,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/amplify-table-4.test.ts - CLI_REGION: eu-west-2 + CLI_REGION: eu-west-3 depend-on: - publish_to_local_registry - identifier: amplify_table_3 @@ -1101,7 +1111,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/amplify-table-3.test.ts - CLI_REGION: eu-west-3 + CLI_REGION: me-south-1 depend-on: - publish_to_local_registry - identifier: amplify_table_2 @@ -1111,7 +1121,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/amplify-table-2.test.ts - CLI_REGION: me-south-1 + CLI_REGION: sa-east-1 depend-on: - publish_to_local_registry - identifier: amplify_table_1 @@ -1121,7 +1131,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/amplify-table-1.test.ts - CLI_REGION: sa-east-1 + CLI_REGION: us-east-1 depend-on: - publish_to_local_registry - identifier: amplify_ddb_canary @@ -1131,7 +1141,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/amplify-ddb-canary.test.ts - CLI_REGION: us-east-1 + CLI_REGION: us-east-2 depend-on: - publish_to_local_registry - identifier: all_auth_modes @@ -1141,7 +1151,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/all-auth-modes.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-1 depend-on: - publish_to_local_registry - identifier: admin_role @@ -1151,7 +1161,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/admin-role.test.ts - CLI_REGION: us-west-1 + CLI_REGION: us-west-2 depend-on: - publish_to_local_registry - identifier: sql_custom_ssl @@ -1161,7 +1171,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/sql-custom-ssl/sql-custom-ssl.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: restricted_field_auth_gen2 @@ -1172,7 +1182,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/restricted-field-auth/restricted-field-auth-gen2.test.ts - CLI_REGION: ap-south-1 + CLI_REGION: ap-southeast-1 depend-on: - publish_to_local_registry - identifier: restricted_field_auth_gen1 @@ -1183,7 +1193,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/restricted-field-auth/restricted-field-auth-gen1.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 depend-on: - publish_to_local_registry - identifier: restricted_field_auth_gen2_subscriptions_off @@ -1194,7 +1204,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/restricted-field-auth/subscriptions-off/restricted-field-auth-gen2-subscriptions-off.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: ca-central-1 depend-on: - publish_to_local_registry - identifier: restricted_field_auth_gen1_subscriptions_off @@ -1205,7 +1215,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/restricted-field-auth/subscriptions-off/restricted-field-auth-gen1-subscriptions-off.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: references_sqlprimary_sqlrelated @@ -1216,7 +1226,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/references/references-sqlprimary-sqlrelated.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 depend-on: - publish_to_local_registry - identifier: references_sqlprimary_ddbrelated @@ -1227,7 +1237,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/references/references-sqlprimary-ddbrelated.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: ca-central-1 depend-on: - publish_to_local_registry - identifier: references_ddbprimary_sqlrelated @@ -1238,7 +1248,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/references/references-ddbprimary-sqlrelated.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: references_ddbprimary_ddbrelated @@ -1249,7 +1259,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/references/references-ddbprimary-ddbrelated.test.ts - CLI_REGION: eu-central-1 + CLI_REGION: eu-north-1 depend-on: - publish_to_local_registry - identifier: recursive_relationships_sql @@ -1260,7 +1270,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/recursive/recursive-relationships-sql.test.ts - CLI_REGION: eu-north-1 + CLI_REGION: eu-south-1 depend-on: - publish_to_local_registry - identifier: recursive_relationships_ddb @@ -1271,7 +1281,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/recursive/recursive-relationships-ddb.test.ts - CLI_REGION: eu-south-1 + CLI_REGION: eu-west-1 depend-on: - publish_to_local_registry - identifier: uuid_pk_sqlprimary_sqlrelated @@ -1282,7 +1292,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/postgres-uuid-pk/uuid-pk-sqlprimary-sqlrelated.test.ts - CLI_REGION: eu-west-1 + CLI_REGION: eu-west-2 depend-on: - publish_to_local_registry - identifier: uuid_pk_sqlprimary_ddbrelated @@ -1293,7 +1303,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/postgres-uuid-pk/uuid-pk-sqlprimary-ddbrelated.test.ts - CLI_REGION: eu-west-2 + CLI_REGION: eu-west-3 depend-on: - publish_to_local_registry - identifier: uuid_pk_ddbprimary_sqlrelated @@ -1304,7 +1314,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/postgres-uuid-pk/uuid-pk-ddbprimary-sqlrelated.test.ts - CLI_REGION: eu-west-3 + CLI_REGION: me-south-1 depend-on: - publish_to_local_registry - identifier: multi_relationship_sqlprimary_sqlrelated @@ -1315,7 +1325,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/multi-relationship/multi-relationship-sqlprimary-sqlrelated.test.ts - CLI_REGION: me-south-1 + CLI_REGION: sa-east-1 depend-on: - publish_to_local_registry - identifier: multi_relationship_sqlprimary_ddbrelated @@ -1326,7 +1336,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/multi-relationship/multi-relationship-sqlprimary-ddbrelated.test.ts - CLI_REGION: sa-east-1 + CLI_REGION: us-east-1 depend-on: - publish_to_local_registry - identifier: multi_relationship_ddbprimary_sqlrelated @@ -1337,7 +1347,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/multi-relationship/multi-relationship-ddbprimary-sqlrelated.test.ts - CLI_REGION: us-east-1 + CLI_REGION: us-east-2 depend-on: - publish_to_local_registry - identifier: multi_relationship_ddbprimary_ddbrelated @@ -1348,7 +1358,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/relationships/multi-relationship/multi-relationship-ddbprimary-ddbrelated.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-1 depend-on: - publish_to_local_registry - identifier: relationships_gen1 @@ -1358,7 +1368,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/relationships/gen1/relationships-gen1.test.ts - CLI_REGION: us-west-1 + CLI_REGION: us-west-2 depend-on: - publish_to_local_registry - identifier: assoc_field_subscriptions_off @@ -1369,7 +1379,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/owner-auth/subscriptions-off/assoc-field-subscriptions-off.test.ts - CLI_REGION: ap-south-1 + CLI_REGION: ap-southeast-1 depend-on: - publish_to_local_registry - identifier: bind_sql_ids @@ -1379,7 +1389,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/owner-auth/bind-sql-ids/bind-sql-ids.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 depend-on: - publish_to_local_registry - identifier: assoc_field_sqlprimary_sqlrelated @@ -1390,7 +1400,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/owner-auth/assoc-field/assoc-field-sqlprimary-sqlrelated.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: ca-central-1 depend-on: - publish_to_local_registry - identifier: assoc_field_sqlprimary_ddbrelated @@ -1401,7 +1411,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/owner-auth/assoc-field/assoc-field-sqlprimary-ddbrelated.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: assoc_field_ddbprimary_sqlrelated @@ -1412,7 +1422,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/owner-auth/assoc-field/assoc-field-ddbprimary-sqlrelated.test.ts - CLI_REGION: eu-central-1 + CLI_REGION: eu-north-1 depend-on: - publish_to_local_registry - identifier: assoc_field_ddbprimary_ddbrelated @@ -1423,7 +1433,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/owner-auth/assoc-field/assoc-field-ddbprimary-ddbrelated.test.ts - CLI_REGION: eu-north-1 + CLI_REGION: eu-south-1 depend-on: - publish_to_local_registry - identifier: static_group_auth_sqlprimary_sqlrelated_subscriptions_off @@ -1434,7 +1444,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/static-group-auth/static-group-auth-sqlprimary-sqlrelated-subscriptions-off.test.ts - CLI_REGION: eu-south-1 + CLI_REGION: eu-west-1 depend-on: - publish_to_local_registry - identifier: static_group_auth_sqlprimary_ddbrelated_subscriptions_off @@ -1445,7 +1455,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/static-group-auth/static-group-auth-sqlprimary-ddbrelated-subscriptions-off.test.ts - CLI_REGION: eu-west-1 + CLI_REGION: eu-west-2 depend-on: - publish_to_local_registry - identifier: static_group_auth_ddbprimary_sqlrelated_subscriptions_off @@ -1456,7 +1466,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/static-group-auth/static-group-auth-ddbprimary-sqlrelated-subscriptions-off.test.ts - CLI_REGION: eu-west-2 + CLI_REGION: eu-west-3 depend-on: - publish_to_local_registry - identifier: static_group_auth_ddbprimary_ddbrelated_subscriptions_off @@ -1467,7 +1477,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/static-group-auth/static-group-auth-ddbprimary-ddbrelated-subscriptions-off.test.ts - CLI_REGION: eu-west-3 + CLI_REGION: me-south-1 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_sqlprimary_sqlrelated_subscriptions_off @@ -1478,7 +1488,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/dynamic-group-auth/dynamic-group-auth-sqlprimary-sqlrelated-subscriptions-off.test.ts - CLI_REGION: me-south-1 + CLI_REGION: sa-east-1 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_sqlprimary_ddbrelated_subscriptions_off @@ -1489,7 +1499,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/dynamic-group-auth/dynamic-group-auth-sqlprimary-ddbrelated-subscriptions-off.test.ts - CLI_REGION: sa-east-1 + CLI_REGION: us-east-1 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_ddbprimary_sqlrelated_subscriptions_off @@ -1500,7 +1510,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/dynamic-group-auth/dynamic-group-auth-ddbprimary-sqlrelated-subscriptions-off.test.ts - CLI_REGION: us-east-1 + CLI_REGION: us-east-2 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_ddbprimary_ddbrelated_subscriptions_off @@ -1511,7 +1521,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/subscriptions-off/dynamic-group-auth/dynamic-group-auth-ddbprimary-ddbrelated-subscriptions-off.test.ts - CLI_REGION: us-east-2 + CLI_REGION: us-west-1 depend-on: - publish_to_local_registry - identifier: static_group_auth_sqlprimary_sqlrelated @@ -1522,7 +1532,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/static-group-auth/static-group-auth-sqlprimary-sqlrelated.test.ts - CLI_REGION: us-west-1 + CLI_REGION: us-west-2 depend-on: - publish_to_local_registry - identifier: static_group_auth_sqlprimary_ddbrelated @@ -1533,7 +1543,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/static-group-auth/static-group-auth-sqlprimary-ddbrelated.test.ts - CLI_REGION: us-west-2 + CLI_REGION: ap-northeast-1 depend-on: - publish_to_local_registry - identifier: static_group_auth_ddbprimary_sqlrelated @@ -1544,7 +1554,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/static-group-auth/static-group-auth-ddbprimary-sqlrelated.test.ts - CLI_REGION: ap-northeast-1 + CLI_REGION: ap-northeast-2 depend-on: - publish_to_local_registry - identifier: static_group_auth_ddbprimary_ddbrelated @@ -1555,7 +1565,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/static-group-auth/static-group-auth-ddbprimary-ddbrelated.test.ts - CLI_REGION: ap-northeast-2 + CLI_REGION: ap-south-1 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_sqlprimary_sqlrelated @@ -1566,7 +1576,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/dynamic-group-auth/dynamic-group-auth-sqlprimary-sqlrelated.test.ts - CLI_REGION: ap-south-1 + CLI_REGION: ap-southeast-1 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_sqlprimary_ddbrelated @@ -1577,7 +1587,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/dynamic-group-auth/dynamic-group-auth-sqlprimary-ddbrelated.test.ts - CLI_REGION: ap-southeast-1 + CLI_REGION: ap-southeast-2 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_ddbprimary_sqlrelated @@ -1588,7 +1598,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/dynamic-group-auth/dynamic-group-auth-ddbprimary-sqlrelated.test.ts - CLI_REGION: ap-southeast-2 + CLI_REGION: ca-central-1 depend-on: - publish_to_local_registry - identifier: dynamic_group_auth_ddbprimary_ddbrelated @@ -1599,7 +1609,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/group-auth/dynamic-group-auth/dynamic-group-auth-ddbprimary-ddbrelated.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: generation @@ -1620,7 +1630,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/deploy-velocity/single-gsi-100k-records.test.ts - CLI_REGION: ca-central-1 + CLI_REGION: eu-central-1 depend-on: - publish_to_local_registry - identifier: replace_2_gsis_update_attr_100k_records @@ -1631,7 +1641,7 @@ batch: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: >- src/__tests__/deploy-velocity/replace-2-gsis-update-attr-100k-records.test.ts - CLI_REGION: eu-central-1 + CLI_REGION: eu-north-1 depend-on: - publish_to_local_registry - identifier: replace_2_gsis_100k_records @@ -1641,7 +1651,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/deploy-velocity/replace-2-gsis-100k-records.test.ts - CLI_REGION: eu-north-1 + CLI_REGION: eu-south-1 depend-on: - publish_to_local_registry - identifier: 3_gsis_100k_records @@ -1651,7 +1661,7 @@ batch: variables: NODE_OPTIONS: '--max-old-space-size=6656' TEST_SUITE: src/__tests__/deploy-velocity/3-gsis-100k-records.test.ts - CLI_REGION: eu-south-1 + CLI_REGION: eu-west-1 depend-on: - publish_to_local_registry - identifier: conversation diff --git a/packages/amplify-graphql-api-construct-tests/src/__tests__/sql-pg-auto-increment.test.ts b/packages/amplify-graphql-api-construct-tests/src/__tests__/sql-pg-auto-increment.test.ts new file mode 100644 index 0000000000..acbdc63398 --- /dev/null +++ b/packages/amplify-graphql-api-construct-tests/src/__tests__/sql-pg-auto-increment.test.ts @@ -0,0 +1,55 @@ +import generator from 'generate-password'; +import { getResourceNamesForStrategyName, ImportedRDSType } from '@aws-amplify/graphql-transformer-core'; +import { getRDSTableNamePrefix } from 'amplify-category-api-e2e-core'; +import { SqlDatatabaseController } from '../sql-datatabase-controller'; +import { DURATION_1_HOUR } from '../utils/duration-constants'; +import { testGraphQLAPIAutoIncrement } from '../sql-tests-common/sql-models-auto-increment'; + +jest.setTimeout(DURATION_1_HOUR); + +describe('CDK GraphQL Transformer deployments with Postgres SQL datasources', () => { + const projFolderName = 'pgmodels'; + + // sufficient password length that meets the requirements for RDS cluster/instance + const [username, password, identifier] = generator.generateMultiple(3, { length: 11 }); + const region = process.env.CLI_REGION ?? 'us-west-2'; + const engine = 'postgres'; + + const databaseController: SqlDatatabaseController = new SqlDatatabaseController( + [ + `CREATE TABLE "${getRDSTableNamePrefix()}coffee_queue" ("orderNumber" SERIAL PRIMARY KEY, "order" VARCHAR(256) NOT NULL, "customer" VARCHAR(256))`, + ], + { + identifier, + engine, + username, + password, + region, + }, + ); + + const strategyName = `${engine}DBStrategy`; + const resourceNames = getResourceNamesForStrategyName(strategyName); + + beforeAll(async () => { + await databaseController.setupDatabase(); + }); + + afterAll(async () => { + await databaseController.cleanupDatabase(); + }); + + const constructTestOptions = (connectionConfigName: string) => ({ + projFolderName, + region, + connectionConfigName, + dbController: databaseController, + resourceNames, + }); + + testGraphQLAPIAutoIncrement( + constructTestOptions('connectionUri'), + 'creates a GraphQL API from SQL-based models using Connection String SSM parameter', + ImportedRDSType.POSTGRESQL, + ); +}); diff --git a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-auto-increment/field-map.ts b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-auto-increment/field-map.ts new file mode 100644 index 0000000000..92ddfb51cb --- /dev/null +++ b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-auto-increment/field-map.ts @@ -0,0 +1,7 @@ +import { FieldMap } from '../../../utils/sql-crudl-helper'; + +export const coffeeQueueFieldMap: FieldMap = { + orderNumber: true, + order: true, + customer: true, +}; diff --git a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-auto-increment/schema.graphql b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-auto-increment/schema.graphql new file mode 100644 index 0000000000..3a762837df --- /dev/null +++ b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-auto-increment/schema.graphql @@ -0,0 +1,5 @@ +type CoffeeQueue @model @refersTo(name: "e2e_test_coffee_queue") { + orderNumber: Int! @primaryKey @default + order: String! + customer: String +} diff --git a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-models/schema.graphql b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-models/schema.graphql index b99fe7eeb8..e5138d8a6a 100644 --- a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-models/schema.graphql +++ b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/schemas/sql-models/schema.graphql @@ -1,6 +1,6 @@ type Todo @model @refersTo(name: "e2e_test_todos") { id: ID! @primaryKey - description: String! + description: String! @default(value: "Lorem ipsum yadda yadda...") } type Student @model @refersTo(name: "e2e_test_students") { studentId: Int! @primaryKey(sortKeyFields: ["classId"]) diff --git a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models-auto-increment.ts b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models-auto-increment.ts new file mode 100644 index 0000000000..c30d1187f7 --- /dev/null +++ b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models-auto-increment.ts @@ -0,0 +1,174 @@ +import * as path from 'path'; +import * as fs from 'fs-extra'; +import { ImportedRDSType } from '@aws-amplify/graphql-transformer-core'; +import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync'; +import { createNewProjectDir, deleteProjectDir, getRDSTableNamePrefix } from 'amplify-category-api-e2e-core'; +import { initCDKProject, cdkDeploy, cdkDestroy } from '../commands'; +import { SqlDatatabaseController } from '../sql-datatabase-controller'; +import { CRUDLHelper } from '../utils/sql-crudl-helper'; +import { ONE_MINUTE } from '../utils/duration-constants'; +import { coffeeQueueFieldMap } from './schemas/sql-auto-increment/field-map'; + +export const testGraphQLAPIAutoIncrement = ( + options: { + projFolderName: string; + region: string; + connectionConfigName: string; + dbController: SqlDatatabaseController; + resourceNames: { sqlLambdaAliasName: string }; + }, + testBlockDescription: string, + engine: ImportedRDSType, +): void => { + describe(`${testBlockDescription} - ${engine}`, () => { + // In particular, we want to verify that the new CREATE operation + // is allowed to omit the primary key field, and that the primary key + // we get back is the correct, db generated value. + // NOTE: Expects underlying orderNumber column to be a serial primary key in Postgres table + const schemaPath = path.resolve(path.join(__dirname, '..', 'sql-tests-common', 'schemas', 'sql-auto-increment', 'schema.graphql')); + const schemaConfigString = fs.readFileSync(schemaPath).toString(); + const { projFolderName, region, connectionConfigName, dbController } = options; + const templatePath = path.resolve(path.join(__dirname, '..', '__tests__', 'backends', 'sql-models')); + + let projRoot: string; + let name: string; + let outputs: Promise; + let coffeeQueueTableCRUDLHelper: CRUDLHelper; + + beforeAll(async () => { + projRoot = await createNewProjectDir(projFolderName); + name = await initCDKProject(projRoot, templatePath); + dbController.writeDbDetails(projRoot, connectionConfigName, schemaConfigString); + outputs = await cdkDeploy(projRoot, '--all', { postDeployWaitMs: ONE_MINUTE }); + const { awsAppsyncApiEndpoint: apiEndpoint, awsAppsyncApiKey: apiKey } = outputs[name]; + + const appSyncClient = new AWSAppSyncClient({ + url: apiEndpoint, + region, + disableOffline: true, + auth: { + type: AUTH_TYPE.API_KEY, + apiKey, + }, + }); + + coffeeQueueTableCRUDLHelper = new CRUDLHelper(appSyncClient, 'CoffeeQueue', 'CoffeeQueues', coffeeQueueFieldMap); + }); + + afterAll(async () => { + try { + await cdkDestroy(projRoot, '--all'); + await dbController.clearDatabase(); + } catch (err) { + console.log(`Error invoking 'cdk destroy': ${err}`); + } + + deleteProjectDir(projRoot); + }); + + test(`check CRUDL on coffee queue table with auto increment primary key - ${engine}`, async () => { + // Order Coffee Mutation + const createCoffeeOrder1 = await coffeeQueueTableCRUDLHelper.create({ customer: 'petesv', order: 'cold brew' }); + + expect(createCoffeeOrder1).toBeDefined(); + expect(createCoffeeOrder1.orderNumber).toBeDefined(); + expect(createCoffeeOrder1.customer).toEqual('petesv'); + expect(createCoffeeOrder1.order).toEqual('cold brew'); + + // Get Todo Query + const getCoffeeOrder1 = await coffeeQueueTableCRUDLHelper.get({ orderNumber: createCoffeeOrder1.orderNumber }); + + expect(getCoffeeOrder1.orderNumber).toEqual(createCoffeeOrder1.orderNumber); + expect(getCoffeeOrder1.customer).toEqual(createCoffeeOrder1.customer); + + // Update Todo Mutation + const updateCoffeeOrder1 = await coffeeQueueTableCRUDLHelper.update({ + orderNumber: createCoffeeOrder1.orderNumber, + customer: 'petesv', + order: 'hot brew', + }); + + expect(updateCoffeeOrder1.orderNumber).toEqual(createCoffeeOrder1.orderNumber); + expect(updateCoffeeOrder1.order).toEqual('hot brew'); + + // Get Todo Query after update + const getUpdatedCoffeeOrder1 = await coffeeQueueTableCRUDLHelper.get({ orderNumber: createCoffeeOrder1.orderNumber }); + + expect(getUpdatedCoffeeOrder1.orderNumber).toEqual(createCoffeeOrder1.orderNumber); + expect(getUpdatedCoffeeOrder1.order).toEqual('hot brew'); + + // List Todo Query & Create with custom SERIAL field value + const customOrderNumber = 42; + const createCofffeeOrder2 = await coffeeQueueTableCRUDLHelper.create({ orderNumber: customOrderNumber, order: 'latte' }); + expect(createCofffeeOrder2.orderNumber).toEqual(customOrderNumber); + + const listTodo = await coffeeQueueTableCRUDLHelper.list(); + expect(listTodo.items.length).toEqual(2); + expect(listTodo.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + orderNumber: getUpdatedCoffeeOrder1.orderNumber, + order: 'hot brew', + }), + expect.objectContaining({ + orderNumber: createCofffeeOrder2.orderNumber, + order: 'latte', + }), + ]), + ); + + // Delete Todo Mutation + const deleteCoffeeOrder1 = await coffeeQueueTableCRUDLHelper.delete({ orderNumber: createCoffeeOrder1.orderNumber }); + + expect(deleteCoffeeOrder1.orderNumber).toEqual(createCoffeeOrder1.orderNumber); + expect(deleteCoffeeOrder1.order).toEqual('hot brew'); + + const getDeletedCoffeeOrder1 = await coffeeQueueTableCRUDLHelper.get({ orderNumber: createCoffeeOrder1.orderNumber }); + + expect(getDeletedCoffeeOrder1).toBeNull(); + + // List Todo Query after delete + const listCoffeeOrdersAfterDelete = await coffeeQueueTableCRUDLHelper.list(); + + expect(listCoffeeOrdersAfterDelete.items.length).toEqual(1); + expect(listCoffeeOrdersAfterDelete.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + orderNumber: createCofffeeOrder2.orderNumber, + order: 'latte', + }), + ]), + ); + + // Check invalid CRUD operation returns generic error message + const createTodo6 = await coffeeQueueTableCRUDLHelper.create({ order: 'mocha' }); + + try { + // Invalid because the pk (orderNumber) already exists + await coffeeQueueTableCRUDLHelper.create({ orderNumber: createTodo6.orderNumber, order: 'americano' }); + } catch (error) { + coffeeQueueTableCRUDLHelper.checkGenericError(error?.message); + } + + const biggerThanAnyExistingOrderNumber = 99999999; + + try { + await coffeeQueueTableCRUDLHelper.get({ orderNumber: biggerThanAnyExistingOrderNumber }); + } catch (error) { + coffeeQueueTableCRUDLHelper.checkGenericError(error?.message); + } + + try { + await coffeeQueueTableCRUDLHelper.update({ orderNumber: biggerThanAnyExistingOrderNumber, order: 'cortado' }); + } catch (error) { + coffeeQueueTableCRUDLHelper.checkGenericError(error?.message); + } + + try { + await coffeeQueueTableCRUDLHelper.delete({ orderNumber: biggerThanAnyExistingOrderNumber }); + } catch (error) { + coffeeQueueTableCRUDLHelper.checkGenericError(error?.message); + } + }); + }); +}; diff --git a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models.ts b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models.ts index 0411dd2290..0e58d04213 100644 --- a/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models.ts +++ b/packages/amplify-graphql-api-construct-tests/src/sql-tests-common/sql-models.ts @@ -73,6 +73,34 @@ export const testGraphQLAPI = ( deleteProjectDir(projRoot); }); + test(`check default value on todo table - ${engine}`, async () => { + const defaultTodoDescription = 'Lorem ipsum yadda yadda...'; + + // Create Todo Mutation + const createTodo1 = await toDoTableCRUDLHelper.create({}); + + expect(createTodo1).toBeDefined(); + expect(createTodo1.id).toBeDefined(); + expect(createTodo1.description).toEqual(defaultTodoDescription); + + // Get Todo Query + const getTodo1 = await toDoTableCRUDLHelper.getById(createTodo1.id); + expect(getTodo1.id).toEqual(createTodo1.id); + expect(getTodo1.description).toEqual(createTodo1.description); + + // Update Todo Mutation + const updateTodo1 = await toDoTableCRUDLHelper.update({ id: createTodo1.id, description: 'Updated Todo #1' }); + + expect(updateTodo1.id).toEqual(createTodo1.id); + expect(updateTodo1.description).toEqual('Updated Todo #1'); + + // Get Todo Query after update + const getUpdatedTodo1 = await toDoTableCRUDLHelper.getById(createTodo1.id); + + expect(getUpdatedTodo1.id).toEqual(createTodo1.id); + expect(getUpdatedTodo1.description).toEqual('Updated Todo #1'); + }); + test(`check CRUDL on todo table with default primary key - ${engine}`, async () => { // Create Todo Mutation const createTodo1 = await toDoTableCRUDLHelper.create({ description: 'Todo #1' }); diff --git a/packages/amplify-graphql-default-value-transformer/package.json b/packages/amplify-graphql-default-value-transformer/package.json index e6bf424dfa..d38613a33e 100644 --- a/packages/amplify-graphql-default-value-transformer/package.json +++ b/packages/amplify-graphql-default-value-transformer/package.json @@ -75,7 +75,8 @@ "src/**/*.ts" ], "coveragePathIgnorePatterns": [ - "/__tests__/" + "/__tests__/", + "types.ts" ], "snapshotFormat": { "escapeString": true, diff --git a/packages/amplify-graphql-default-value-transformer/src/__tests__/__snapshots__/amplify-grapphql-default-value-transformer.test.ts.snap b/packages/amplify-graphql-default-value-transformer/src/__tests__/__snapshots__/amplify-grapphql-default-value-transformer.test.ts.snap index 320a27b4e9..e37213cd0a 100644 --- a/packages/amplify-graphql-default-value-transformer/src/__tests__/__snapshots__/amplify-grapphql-default-value-transformer.test.ts.snap +++ b/packages/amplify-graphql-default-value-transformer/src/__tests__/__snapshots__/amplify-grapphql-default-value-transformer.test.ts.snap @@ -1,5 +1,227 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`DefaultValueModelTransformer: should allow auto-increment primary key 1`] = ` +"type CoffeeQueue { + orderNumber: Int! + order: String! + customer: String +} + +input ModelStringInput { + ne: String + eq: String + le: String + lt: String + ge: String + gt: String + contains: String + notContains: String + between: [String] + beginsWith: String + attributeExists: Boolean + attributeType: ModelAttributeTypes + size: ModelSizeInput +} + +input ModelIntInput { + ne: Int + eq: Int + le: Int + lt: Int + ge: Int + gt: Int + between: [Int] + attributeExists: Boolean + attributeType: ModelAttributeTypes +} + +input ModelFloatInput { + ne: Float + eq: Float + le: Float + lt: Float + ge: Float + gt: Float + between: [Float] + attributeExists: Boolean + attributeType: ModelAttributeTypes +} + +input ModelBooleanInput { + ne: Boolean + eq: Boolean + attributeExists: Boolean + attributeType: ModelAttributeTypes +} + +input ModelIDInput { + ne: ID + eq: ID + le: ID + lt: ID + ge: ID + gt: ID + contains: ID + notContains: ID + between: [ID] + beginsWith: ID + attributeExists: Boolean + attributeType: ModelAttributeTypes + size: ModelSizeInput +} + +input ModelSubscriptionStringInput { + ne: String + eq: String + le: String + lt: String + ge: String + gt: String + contains: String + notContains: String + between: [String] + beginsWith: String + in: [String] + notIn: [String] +} + +input ModelSubscriptionIntInput { + ne: Int + eq: Int + le: Int + lt: Int + ge: Int + gt: Int + between: [Int] + in: [Int] + notIn: [Int] +} + +input ModelSubscriptionFloatInput { + ne: Float + eq: Float + le: Float + lt: Float + ge: Float + gt: Float + between: [Float] + in: [Float] + notIn: [Float] +} + +input ModelSubscriptionBooleanInput { + ne: Boolean + eq: Boolean +} + +input ModelSubscriptionIDInput { + ne: ID + eq: ID + le: ID + lt: ID + ge: ID + gt: ID + contains: ID + notContains: ID + between: [ID] + beginsWith: ID + in: [ID] + notIn: [ID] +} + +enum ModelAttributeTypes { + binary + binarySet + bool + list + map + number + numberSet + string + stringSet + _null +} + +input ModelSizeInput { + ne: Int + eq: Int + le: Int + lt: Int + ge: Int + gt: Int + between: [Int] +} + +enum ModelSortDirection { + ASC + DESC +} + +type ModelCoffeeQueueConnection { + items: [CoffeeQueue]! + nextToken: String +} + +input ModelCoffeeQueueFilterInput { + orderNumber: ModelIntInput + order: ModelStringInput + customer: ModelStringInput + and: [ModelCoffeeQueueFilterInput] + or: [ModelCoffeeQueueFilterInput] + not: ModelCoffeeQueueFilterInput +} + +type Query { + getCoffeeQueue(orderNumber: Int!): CoffeeQueue + listCoffeeQueues(orderNumber: Int, filter: ModelCoffeeQueueFilterInput, limit: Int, nextToken: String, sortDirection: ModelSortDirection): ModelCoffeeQueueConnection +} + +input ModelCoffeeQueueConditionInput { + order: ModelStringInput + customer: ModelStringInput + and: [ModelCoffeeQueueConditionInput] + or: [ModelCoffeeQueueConditionInput] + not: ModelCoffeeQueueConditionInput +} + +input CreateCoffeeQueueInput { + orderNumber: Int + order: String! + customer: String +} + +input UpdateCoffeeQueueInput { + orderNumber: Int! + order: String + customer: String +} + +input DeleteCoffeeQueueInput { + orderNumber: Int! +} + +type Mutation { + createCoffeeQueue(input: CreateCoffeeQueueInput!, condition: ModelCoffeeQueueConditionInput): CoffeeQueue + updateCoffeeQueue(input: UpdateCoffeeQueueInput!, condition: ModelCoffeeQueueConditionInput): CoffeeQueue + deleteCoffeeQueue(input: DeleteCoffeeQueueInput!, condition: ModelCoffeeQueueConditionInput): CoffeeQueue +} + +input ModelSubscriptionCoffeeQueueFilterInput { + orderNumber: ModelSubscriptionIntInput + order: ModelSubscriptionStringInput + customer: ModelSubscriptionStringInput + and: [ModelSubscriptionCoffeeQueueFilterInput] + or: [ModelSubscriptionCoffeeQueueFilterInput] +} + +type Subscription { + onCreateCoffeeQueue(filter: ModelSubscriptionCoffeeQueueFilterInput): CoffeeQueue @aws_subscribe(mutations: [\\"createCoffeeQueue\\"]) + onUpdateCoffeeQueue(filter: ModelSubscriptionCoffeeQueueFilterInput): CoffeeQueue @aws_subscribe(mutations: [\\"updateCoffeeQueue\\"]) + onDeleteCoffeeQueue(filter: ModelSubscriptionCoffeeQueueFilterInput): CoffeeQueue @aws_subscribe(mutations: [\\"deleteCoffeeQueue\\"]) +} +" +`; + exports[`DefaultValueModelTransformer: should be supported on a required field. 1`] = ` "type Test { id: ID! @@ -231,6 +453,222 @@ $util.qr($context.args.input.put(\\"stringValue\\", $util.defaultIfNull($ctx.arg {}" `; +exports[`DefaultValueModelTransformer: should successfully transform a simple schema with @default 1`] = ` +"type TestAutoIncrement { + id: ID! + value: Int +} + +input ModelStringInput { + ne: String + eq: String + le: String + lt: String + ge: String + gt: String + contains: String + notContains: String + between: [String] + beginsWith: String + attributeExists: Boolean + attributeType: ModelAttributeTypes + size: ModelSizeInput +} + +input ModelIntInput { + ne: Int + eq: Int + le: Int + lt: Int + ge: Int + gt: Int + between: [Int] + attributeExists: Boolean + attributeType: ModelAttributeTypes +} + +input ModelFloatInput { + ne: Float + eq: Float + le: Float + lt: Float + ge: Float + gt: Float + between: [Float] + attributeExists: Boolean + attributeType: ModelAttributeTypes +} + +input ModelBooleanInput { + ne: Boolean + eq: Boolean + attributeExists: Boolean + attributeType: ModelAttributeTypes +} + +input ModelIDInput { + ne: ID + eq: ID + le: ID + lt: ID + ge: ID + gt: ID + contains: ID + notContains: ID + between: [ID] + beginsWith: ID + attributeExists: Boolean + attributeType: ModelAttributeTypes + size: ModelSizeInput +} + +input ModelSubscriptionStringInput { + ne: String + eq: String + le: String + lt: String + ge: String + gt: String + contains: String + notContains: String + between: [String] + beginsWith: String + in: [String] + notIn: [String] +} + +input ModelSubscriptionIntInput { + ne: Int + eq: Int + le: Int + lt: Int + ge: Int + gt: Int + between: [Int] + in: [Int] + notIn: [Int] +} + +input ModelSubscriptionFloatInput { + ne: Float + eq: Float + le: Float + lt: Float + ge: Float + gt: Float + between: [Float] + in: [Float] + notIn: [Float] +} + +input ModelSubscriptionBooleanInput { + ne: Boolean + eq: Boolean +} + +input ModelSubscriptionIDInput { + ne: ID + eq: ID + le: ID + lt: ID + ge: ID + gt: ID + contains: ID + notContains: ID + between: [ID] + beginsWith: ID + in: [ID] + notIn: [ID] +} + +enum ModelAttributeTypes { + binary + binarySet + bool + list + map + number + numberSet + string + stringSet + _null +} + +input ModelSizeInput { + ne: Int + eq: Int + le: Int + lt: Int + ge: Int + gt: Int + between: [Int] +} + +enum ModelSortDirection { + ASC + DESC +} + +type ModelTestAutoIncrementConnection { + items: [TestAutoIncrement]! + nextToken: String +} + +input ModelTestAutoIncrementFilterInput { + id: ModelIDInput + value: ModelIntInput + and: [ModelTestAutoIncrementFilterInput] + or: [ModelTestAutoIncrementFilterInput] + not: ModelTestAutoIncrementFilterInput +} + +type Query { + getTestAutoIncrement(id: ID!): TestAutoIncrement + listTestAutoIncrements(id: ID, filter: ModelTestAutoIncrementFilterInput, limit: Int, nextToken: String, sortDirection: ModelSortDirection): ModelTestAutoIncrementConnection +} + +input ModelTestAutoIncrementConditionInput { + value: ModelIntInput + and: [ModelTestAutoIncrementConditionInput] + or: [ModelTestAutoIncrementConditionInput] + not: ModelTestAutoIncrementConditionInput +} + +input CreateTestAutoIncrementInput { + id: ID + value: Int +} + +input UpdateTestAutoIncrementInput { + id: ID! + value: Int +} + +input DeleteTestAutoIncrementInput { + id: ID! +} + +type Mutation { + createTestAutoIncrement(input: CreateTestAutoIncrementInput!, condition: ModelTestAutoIncrementConditionInput): TestAutoIncrement + updateTestAutoIncrement(input: UpdateTestAutoIncrementInput!, condition: ModelTestAutoIncrementConditionInput): TestAutoIncrement + deleteTestAutoIncrement(input: DeleteTestAutoIncrementInput!, condition: ModelTestAutoIncrementConditionInput): TestAutoIncrement +} + +input ModelSubscriptionTestAutoIncrementFilterInput { + id: ModelSubscriptionIDInput + value: ModelSubscriptionIntInput + and: [ModelSubscriptionTestAutoIncrementFilterInput] + or: [ModelSubscriptionTestAutoIncrementFilterInput] +} + +type Subscription { + onCreateTestAutoIncrement(filter: ModelSubscriptionTestAutoIncrementFilterInput): TestAutoIncrement @aws_subscribe(mutations: [\\"createTestAutoIncrement\\"]) + onUpdateTestAutoIncrement(filter: ModelSubscriptionTestAutoIncrementFilterInput): TestAutoIncrement @aws_subscribe(mutations: [\\"updateTestAutoIncrement\\"]) + onDeleteTestAutoIncrement(filter: ModelSubscriptionTestAutoIncrementFilterInput): TestAutoIncrement @aws_subscribe(mutations: [\\"deleteTestAutoIncrement\\"]) +} +" +`; + exports[`DefaultValueModelTransformer: should successfully transform simple valid schema 1`] = ` "type Post { id: ID! diff --git a/packages/amplify-graphql-default-value-transformer/src/__tests__/amplify-grapphql-default-value-transformer.test.ts b/packages/amplify-graphql-default-value-transformer/src/__tests__/amplify-grapphql-default-value-transformer.test.ts index 344b350d5f..48861f3f13 100644 --- a/packages/amplify-graphql-default-value-transformer/src/__tests__/amplify-grapphql-default-value-transformer.test.ts +++ b/packages/amplify-graphql-default-value-transformer/src/__tests__/amplify-grapphql-default-value-transformer.test.ts @@ -1,5 +1,12 @@ import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; -import { constructDataSourceStrategies, getResourceNamesForStrategy, validateModelSchema } from '@aws-amplify/graphql-transformer-core'; +import { + constructDataSourceStrategies, + DDB_DEFAULT_DATASOURCE_STRATEGY, + getResourceNamesForStrategy, + MYSQL_DB_TYPE, + POSTGRES_DB_TYPE, + validateModelSchema, +} from '@aws-amplify/graphql-transformer-core'; import { parse } from 'graphql'; import { mockSqlDataSourceStrategy, testTransform } from '@aws-amplify/graphql-transformer-test-utils'; import { PrimaryKeyTransformer } from '@aws-amplify/graphql-index-transformer'; @@ -41,180 +48,35 @@ describe('DefaultValueModelTransformer:', () => { ).toThrow('The @default directive may only be added to scalar or enum field types.'); }); - it('throws if @default is used with a null value', () => { + it.each([ + { + type: 'String', + value: undefined, + expectedError: 'The @default directive requires a value property on non-Postgres datasources.', + }, + { type: 'Int', value: '"text"', expectedError: 'Default value "text" is not a valid Int.' }, + { type: 'Boolean', value: '"text"', expectedError: 'Default value "text" is not a valid Boolean.' }, + { type: 'AWSJSON', value: '"text"', expectedError: 'Default value "text" is not a valid AWSJSON.' }, + { type: 'AWSDate', value: '"text"', expectedError: 'Default value "text" is not a valid AWSDate.' }, + { type: 'AWSDateTime', value: '"text"', expectedError: 'Default value "text" is not a valid AWSDateTime.' }, + { type: 'AWSTime', value: '"text"', expectedError: 'Default value "text" is not a valid AWSTime.' }, + { type: 'AWSTimestamp', value: '"text"', expectedError: 'Default value "text" is not a valid AWSTimestamp.' }, + { type: 'AWSURL', value: '"text"', expectedError: 'Default value "text" is not a valid AWSURL.' }, + { type: 'AWSPhone', value: '"text"', expectedError: 'Default value "text" is not a valid AWSPhone.' }, + { type: 'AWSIPAddress', value: '"text"', expectedError: 'Default value "text" is not a valid AWSIPAddress.' }, + ])(`throws if @default is used with invalid type. %type check.`, ({ type, value, expectedError }) => { const schema = ` type Test @model { - id: ID! - name: String @default - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Directive "@default" argument "value" of type "String!" is required, but it was not provided.'); - }); - - it('throws if @default is used with invalid type. Int check.', () => { - const schema = ` - type Test @model { - id: ID! - value: Int @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid Int.'); - }); - - it('throws if @default is used with invalid type. Boolean check.', () => { - const schema = ` - type Test @model { - id: ID! - value: Boolean @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid Boolean.'); - }); - - it('throws if @default is used with invalid type. AWSJSON check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSJSON @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSJSON.'); - }); - - it('throws if @default is used with invalid type. AWSDate check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSDate @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSDate.'); - }); - - it('throws if @default is used with invalid type. AWSDateTime check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSDateTime @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSDateTime.'); - }); - - it('throws if @default is used with invalid type. AWSTime check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSTime @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSTime.'); - }); - - it('throws if @default is used with invalid type. AWSTimestamp check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSTimestamp @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSTimestamp.'); - }); - - it('throws if @default is used with invalid type. AWSURL check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSURL @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSURL.'); - }); - - it('throws if @default is used with invalid type. AWSPhone check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSPhone @default(value: "text") - } - `; - - expect(() => - testTransform({ - schema, - transformers: [new ModelTransformer(), new DefaultValueTransformer()], - }), - ).toThrow('Default value "text" is not a valid AWSPhone.'); - }); - - it('throws if @default is used with invalid type. AWSIPAddress check.', () => { - const schema = ` - type Test @model { - id: ID! - value: AWSIPAddress @default(value: "text") + id: ID! + value: ${type} ${value !== undefined ? `@default(value: ${value})` : '@default'} } `; - expect(() => testTransform({ schema, transformers: [new ModelTransformer(), new DefaultValueTransformer()], }), - ).toThrow('Default value "text" is not a valid AWSIPAddress.'); + ).toThrow(expectedError); }); it('should validate enum values', async () => { @@ -338,4 +200,106 @@ describe('DefaultValueModelTransformer:', () => { expect(out.resolvers['Mutation.createNote.init.1.req.vtl']).toBeDefined(); expect(out.resolvers['Mutation.createNote.init.2.req.vtl']).toBeUndefined(); }); + + it('cannot use `null` as a default value', () => { + const schema = ` + type CoffeeQueue @model { + id: ID! @primaryKey + orderNumber: Int! @default(value: null) + name: String + }`; + + expect(() => { + testTransform({ + schema: schema, + transformers: [new ModelTransformer(), new DefaultValueTransformer(), new PrimaryKeyTransformer()], + }); + }).toThrow('The @default directive does not support null values.'); + }); + + it.each([ + { strategy: mockSqlDataSourceStrategy({ dbType: MYSQL_DB_TYPE }), datasourceName: 'mysql' }, + { strategy: DDB_DEFAULT_DATASOURCE_STRATEGY, datasourceName: 'DynamoDB (Deafult)' }, + ])('throws if @default (no args) is used non-Postgres datasource (%datasourceName)', ({ strategy }) => { + const schema = ` + type CoffeeQueue @model { + id: ID! @primaryKey + orderNumber: Int! @default + name: String + }`; + expect(() => { + testTransform({ + schema: schema, + transformers: [new ModelTransformer(), new DefaultValueTransformer(), new PrimaryKeyTransformer()], + dataSourceStrategies: constructDataSourceStrategies(schema, strategy), + }); + }).toThrow('The @default directive requires a value property on non-Postgres datasources.'); + }); + + it.each([ + { typeStr: 'Boolean' }, + { typeStr: 'AWSJSON' }, + { typeStr: 'AWSDate' }, + { typeStr: 'AWSDateTime' }, + { typeStr: 'AWSTime' }, + { typeStr: 'AWSTime' }, + { typeStr: 'AWSURL' }, + { typeStr: 'AWSPhone' }, + { typeStr: 'AWSIPAddress' }, + ])('throws if @default (no args) is used on non-int types (case: $typeStr)', ({ typeStr }) => { + const strategy = mockSqlDataSourceStrategy({ dbType: POSTGRES_DB_TYPE }); + + expect(() => { + const schema = ` + type Test @model { + id: ID! @primaryKey + value: ${typeStr} @default + } + `; + testTransform({ + schema, + transformers: [new ModelTransformer(), new DefaultValueTransformer(), new PrimaryKeyTransformer()], + dataSourceStrategies: constructDataSourceStrategies(schema, strategy), + }); + }).toThrow('The @default directive requires a value property on non-Int types.'); + }); + + it('should successfully transform a simple schema with @default', async () => { + const schema = ` + type TestAutoIncrement @model { + id: ID! @primaryKey + value: Int @default + } + `; + const strategy = mockSqlDataSourceStrategy({ dbType: POSTGRES_DB_TYPE }); + const out = testTransform({ + schema, + transformers: [new ModelTransformer(), new DefaultValueTransformer(), new PrimaryKeyTransformer()], + dataSourceStrategies: constructDataSourceStrategies(schema, strategy), + }); + expect(out).toBeDefined(); + expect(out.schema).toMatchSnapshot(); + const parsedSchema = parse(out.schema); + validateModelSchema(parsedSchema); + }); + + it('should allow auto-increment primary key', async () => { + const schema = ` + type CoffeeQueue @model { + orderNumber: Int! @primaryKey @default + order: String! + customer: String + } + `; + const strategy = mockSqlDataSourceStrategy({ dbType: POSTGRES_DB_TYPE }); + const out = testTransform({ + schema, + transformers: [new ModelTransformer(), new DefaultValueTransformer(), new PrimaryKeyTransformer()], + dataSourceStrategies: constructDataSourceStrategies(schema, strategy), + }); + expect(out).toBeDefined(); + expect(out.schema).toMatchSnapshot(); + const parsedSchema = parse(out.schema); + validateModelSchema(parsedSchema); + }); }); diff --git a/packages/amplify-graphql-default-value-transformer/src/graphql-default-value-transformer.ts b/packages/amplify-graphql-default-value-transformer/src/graphql-default-value-transformer.ts index 7178eaad41..2ed188d645 100644 --- a/packages/amplify-graphql-default-value-transformer/src/graphql-default-value-transformer.ts +++ b/packages/amplify-graphql-default-value-transformer/src/graphql-default-value-transformer.ts @@ -4,6 +4,7 @@ import { InputObjectDefinitionWrapper, InvalidDirectiveError, isDynamoDbModel, + isPostgresModel, MappingTemplate, TransformerPluginBase, } from '@aws-amplify/graphql-transformer-core'; @@ -22,7 +23,6 @@ import { Kind, ObjectTypeDefinitionNode, StringValueNode, - TypeNode, } from 'graphql'; import { methodCall, printBlock, qref, raw, ref, str } from 'graphql-mapping-template'; import { getBaseType, isEnum, isListType, isScalarOrEnum, ModelResourceIDs, toCamelCase } from 'graphql-transformer-common'; @@ -31,16 +31,24 @@ import { TypeValidators } from './validators'; const nonStringTypes = ['Int', 'Float', 'Boolean', 'AWSTimestamp', 'AWSJSON']; -const validateFieldType = (ctx: TransformerSchemaVisitStepContextProvider, type: TypeNode): void => { +const validateFieldType = (ctx: TransformerSchemaVisitStepContextProvider, config: DefaultValueDirectiveConfiguration): void => { + const type = config.field.type; + const argc = config.directive.arguments?.length ?? 0; const enums = ctx.output.getTypeDefinitionsOfKind(Kind.ENUM_TYPE_DEFINITION) as EnumTypeDefinitionNode[]; if (isListType(type) || !isScalarOrEnum(type, enums)) { throw new InvalidDirectiveError('The @default directive may only be added to scalar or enum field types.'); } + if (isPostgresModel(ctx, config.object.name.value) && argc === 0 && getBaseType(type) !== 'Int') { + throw new InvalidDirectiveError('The @default directive requires a value property on non-Int types.'); + } }; -const validateDirectiveArguments = (directive: DirectiveNode): void => { - if (directive.arguments!.length === 0) throw new InvalidDirectiveError('The @default directive must have a value property'); - if (directive.arguments!.length > 1) throw new InvalidDirectiveError('The @default directive only takes a value property'); +const validateDirectiveArguments = (ctx: TransformerSchemaVisitStepContextProvider, config: DefaultValueDirectiveConfiguration): void => { + const argc = config.directive.arguments?.length ?? 0; + const isPostgres = isPostgresModel(ctx, config.object.name.value); + if (!isPostgres && argc === 0) + throw new InvalidDirectiveError('The @default directive requires a value property on non-Postgres datasources.'); + if (argc > 1) throw new InvalidDirectiveError('The @default directive only takes a value property'); }; const validateModelDirective = (config: DefaultValueDirectiveConfiguration): void => { @@ -74,8 +82,8 @@ const validateDefaultValueType = (ctx: TransformerSchemaVisitStepContextProvider const validate = (ctx: TransformerSchemaVisitStepContextProvider, config: DefaultValueDirectiveConfiguration): void => { validateModelDirective(config); - validateFieldType(ctx, config.field.type); - validateDirectiveArguments(config.directive); + validateFieldType(ctx, config); + validateDirectiveArguments(ctx, config); // Validate the default values only for the DynamoDB datasource. // For SQL, the database determines and sets the default value. We will not validate the value in transformers. @@ -123,6 +131,10 @@ export class DefaultValueTransformer extends TransformerPluginBase { const input = InputObjectDefinitionWrapper.fromObject(name, config.object, ctx.inputDocument); const fieldWrapper = input.fields.find((f) => f.name === config.field.name.value); fieldWrapper?.makeNullable(); + + if (isPostgresModel(ctx, typeName)) { + ctx.output.updateInput(input.serialize()); + } } } }; diff --git a/packages/amplify-graphql-directives/src/__tests__/__snapshots__/index.test.ts.snap b/packages/amplify-graphql-directives/src/__tests__/__snapshots__/index.test.ts.snap index 8e5b36eecb..c0a20f5bdf 100644 --- a/packages/amplify-graphql-directives/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/amplify-graphql-directives/src/__tests__/__snapshots__/index.test.ts.snap @@ -255,7 +255,7 @@ exports[`Directive Definitions DefaultDirective 1`] = ` Object { "defaults": Object {}, "definition": " - directive @default(value: String!) on FIELD_DEFINITION + directive @default(value: String) on FIELD_DEFINITION ", "name": "default", } diff --git a/packages/amplify-graphql-directives/src/directives/default.ts b/packages/amplify-graphql-directives/src/directives/default.ts index 4aa8c33020..c378c08073 100644 --- a/packages/amplify-graphql-directives/src/directives/default.ts +++ b/packages/amplify-graphql-directives/src/directives/default.ts @@ -2,7 +2,7 @@ import { Directive } from './directive'; const name = 'default'; const definition = /* GraphQL */ ` - directive @${name}(value: String!) on FIELD_DEFINITION + directive @${name}(value: String) on FIELD_DEFINITION `; const defaults = {}; diff --git a/packages/amplify-graphql-transformer-core/API.md b/packages/amplify-graphql-transformer-core/API.md index ad5de175f2..f498d42d04 100644 --- a/packages/amplify-graphql-transformer-core/API.md +++ b/packages/amplify-graphql-transformer-core/API.md @@ -487,6 +487,12 @@ export const isMutationNode: (obj: DefinitionNode) => obj is ObjectTypeDefinitio // @public (undocumented) export const isObjectTypeDefinitionNode: (obj: DefinitionNode) => obj is ObjectTypeDefinitionNode; +// @public (undocumented) +export const isPostgresDbType: (dbType: ModelDataSourceStrategyDbType) => dbType is "POSTGRES"; + +// @public (undocumented) +export const isPostgresModel: (ctx: DataSourceStrategiesProvider, typename: string) => boolean; + // @public (undocumented) export const isQueryNode: (obj: DefinitionNode) => obj is ObjectTypeDefinitionNode | (InterfaceTypeDefinitionNode & { name: { diff --git a/packages/amplify-graphql-transformer-core/src/index.ts b/packages/amplify-graphql-transformer-core/src/index.ts index 999869500d..ac8f8f796b 100644 --- a/packages/amplify-graphql-transformer-core/src/index.ts +++ b/packages/amplify-graphql-transformer-core/src/index.ts @@ -64,6 +64,8 @@ export { isModelType, isMutationNode, isObjectTypeDefinitionNode, + isPostgresDbType, + isPostgresModel, isQueryNode, isQueryType, isSqlDbType, diff --git a/packages/amplify-graphql-transformer-core/src/utils/model-datasource-strategy-utils.ts b/packages/amplify-graphql-transformer-core/src/utils/model-datasource-strategy-utils.ts index 5e460a0b7d..863877e78a 100644 --- a/packages/amplify-graphql-transformer-core/src/utils/model-datasource-strategy-utils.ts +++ b/packages/amplify-graphql-transformer-core/src/utils/model-datasource-strategy-utils.ts @@ -141,6 +141,27 @@ export const isSqlModel = (ctx: DataSourceStrategiesProvider, typename: string): return isSqlDbType(modelDataSourceType.dbType); }; +/** + * Checks if the given model is a PostgreSQL model + * @param ctx Transformer Context + * @param typename Model name + * @returns boolean + */ +export const isPostgresModel = (ctx: DataSourceStrategiesProvider, typename: string): boolean => { + if (isBuiltInGraphqlType(typename)) { + return false; + } + const modelDataSourceType = getModelDataSourceStrategy(ctx, typename); + return isPostgresDbType(modelDataSourceType.dbType); +}; + +/** + * Type predicate that returns true if `dbType` is a PostgreSQL database type + */ +export const isPostgresDbType = (dbType: ModelDataSourceStrategyDbType): dbType is 'POSTGRES' => { + return dbType === POSTGRES_DB_TYPE; +}; + /** * Type predicate that returns true if `obj` is a SQLLambdaModelDataSourceStrategy */