diff --git a/common/changes/@boostercloud/framework-core/migrate_all_2023-04-04-13-13.json b/common/changes/@boostercloud/framework-core/migrate_all_2023-04-04-13-13.json new file mode 100644 index 000000000..c73523bbf --- /dev/null +++ b/common/changes/@boostercloud/framework-core/migrate_all_2023-04-04-13-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@boostercloud/framework-core", + "comment": "Add migrateAll ReadModels", + "type": "minor" + } + ], + "packageName": "@boostercloud/framework-core" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index b0f68e4e9..bcc686a79 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -7,8 +7,8 @@ importers: ../../packages/application-tester: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/jsonwebtoken': ^8.5.8 '@types/node': 16.11.7 @@ -78,10 +78,10 @@ importers: ../../packages/cli: specifiers: - '@boostercloud/application-tester': workspace:^1.6.2 - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-core': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/application-tester': workspace:^1.7.0 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-core': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@oclif/command': ^1.8 '@oclif/config': ^1.18 @@ -197,8 +197,8 @@ importers: ../../packages/framework-common-helpers: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -266,10 +266,10 @@ importers: ../../packages/framework-core: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 - '@boostercloud/metadata-booster': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 + '@boostercloud/metadata-booster': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -365,21 +365,21 @@ importers: ../../packages/framework-integration-tests: specifiers: - '@boostercloud/application-tester': workspace:^1.6.2 - '@boostercloud/cli': workspace:^1.6.2 - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-core': workspace:^1.6.2 - '@boostercloud/framework-provider-aws': workspace:^1.6.2 - '@boostercloud/framework-provider-aws-infrastructure': workspace:^1.6.2 - '@boostercloud/framework-provider-azure': workspace:^1.6.2 - '@boostercloud/framework-provider-azure-infrastructure': workspace:^1.6.2 - '@boostercloud/framework-provider-kubernetes': workspace:^1.6.2 - '@boostercloud/framework-provider-kubernetes-infrastructure': workspace:^1.6.2 - '@boostercloud/framework-provider-local': workspace:^1.6.2 - '@boostercloud/framework-provider-local-infrastructure': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 - '@boostercloud/metadata-booster': workspace:^1.6.2 + '@boostercloud/application-tester': workspace:^1.7.0 + '@boostercloud/cli': workspace:^1.7.0 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-core': workspace:^1.7.0 + '@boostercloud/framework-provider-aws': workspace:^1.7.0 + '@boostercloud/framework-provider-aws-infrastructure': workspace:^1.7.0 + '@boostercloud/framework-provider-azure': workspace:^1.7.0 + '@boostercloud/framework-provider-azure-infrastructure': workspace:^1.7.0 + '@boostercloud/framework-provider-kubernetes': workspace:^1.7.0 + '@boostercloud/framework-provider-kubernetes-infrastructure': workspace:^1.7.0 + '@boostercloud/framework-provider-local': workspace:^1.7.0 + '@boostercloud/framework-provider-local-infrastructure': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 + '@boostercloud/metadata-booster': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@kubernetes/client-node': ^0.17.0 '@types/aws-lambda': 8.10.48 @@ -520,9 +520,9 @@ importers: ../../packages/framework-provider-aws: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/aws-lambda': 8.10.48 '@types/chai': 4.2.18 @@ -616,10 +616,10 @@ importers: '@aws-cdk/core': ^1.170.0 '@aws-cdk/custom-resources': ^1.170.0 '@aws-cdk/cx-api': ^1.170.0 - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-provider-aws': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-provider-aws': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/archiver': 5.1.0 '@types/aws-lambda': 8.10.48 @@ -731,9 +731,9 @@ importers: '@azure/cosmos': ^3.17.0 '@azure/functions': ^1.2.2 '@azure/identity': ~2.1.0 - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -803,11 +803,11 @@ importers: '@azure/arm-resources': ^5.0.1 '@azure/cosmos': ^3.17.0 '@azure/identity': ~2.1.0 - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-core': workspace:^1.6.2 - '@boostercloud/framework-provider-azure': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-core': workspace:^1.7.0 + '@boostercloud/framework-provider-azure': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@cdktf/provider-azurerm': ^0.2.179 '@effect-ts/core': ^0.60.4 '@types/archiver': 5.1.0 @@ -912,9 +912,9 @@ importers: ../../packages/framework-provider-kubernetes: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/body-parser': ~1.19.2 '@types/chai': 4.2.18 @@ -991,9 +991,9 @@ importers: ../../packages/framework-provider-kubernetes-infrastructure: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@kubernetes/client-node': ^0.17.0 '@types/archiver': 5.1.0 @@ -1094,9 +1094,9 @@ importers: ../../packages/framework-provider-local: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -1171,10 +1171,10 @@ importers: ../../packages/framework-provider-local-infrastructure: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/framework-common-helpers': workspace:^1.6.2 - '@boostercloud/framework-provider-local': workspace:^1.6.2 - '@boostercloud/framework-types': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/framework-common-helpers': workspace:^1.7.0 + '@boostercloud/framework-provider-local': workspace:^1.7.0 + '@boostercloud/framework-types': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -1258,8 +1258,8 @@ importers: ../../packages/framework-types: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 - '@boostercloud/metadata-booster': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 + '@boostercloud/metadata-booster': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@effect-ts/node': ~0.39.0 '@types/chai': 4.2.18 @@ -1325,7 +1325,7 @@ importers: ../../packages/metadata-booster: specifiers: - '@boostercloud/eslint-config': workspace:^1.6.2 + '@boostercloud/eslint-config': workspace:^1.7.0 '@effect-ts/core': ^0.60.4 '@types/node': 16.11.7 '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -1530,9 +1530,9 @@ packages: '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq '@aws-cdk/aws-kms': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/aws-lambda': 1.193.0_hjndse4uvjoh2z652h4gibqbfa - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 '@aws-cdk/aws-sns-subscriptions': 1.193.0_7kzgzmrjzvracynar4gtrolizi - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 dev: false @@ -1551,7 +1551,7 @@ packages: '@aws-cdk/aws-elasticloadbalancing': 1.193.0_6pqcupvooeqlpupvlzhdrord2u '@aws-cdk/aws-elasticloadbalancingv2': 1.193.0_gqcd7osjtxd6yzxf76nth7r3mu '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 transitivePeerDependencies: @@ -1600,7 +1600,7 @@ packages: '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq '@aws-cdk/aws-lambda': 1.193.0_hjndse4uvjoh2z652h4gibqbfa '@aws-cdk/aws-s3': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte '@aws-cdk/cx-api': 1.193.0 constructs: 3.4.251 @@ -1898,8 +1898,8 @@ packages: '@aws-cdk/aws-s3-assets': 1.193.0_fl7hneqefdk7a7rv75sucgtmwm '@aws-cdk/aws-secretsmanager': 1.193.0_htsmq7wgkec2uefwunmqkzkjia '@aws-cdk/aws-servicediscovery': 1.193.0_agjehwrl4wnqdjmvscrj7zvbaq - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/aws-ssm': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte '@aws-cdk/cx-api': 1.193.0 @@ -2003,9 +2003,9 @@ packages: '@aws-cdk/aws-kms': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/aws-lambda': 1.193.0_hjndse4uvjoh2z652h4gibqbfa '@aws-cdk/aws-logs': 1.193.0_fl7hneqefdk7a7rv75sucgtmwm - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 '@aws-cdk/aws-sns-subscriptions': 1.193.0_7kzgzmrjzvracynar4gtrolizi - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/aws-stepfunctions': 1.193.0_5prrxj3l5spwy6zim4npjrzbwm '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte '@aws-cdk/custom-resources': 1.193.0_ld77ehigssy773vrtmey3cniuq @@ -2143,9 +2143,9 @@ packages: '@aws-cdk/aws-s3': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq '@aws-cdk/aws-s3-notifications': 1.193.0_4jktmyq4rjkoyfb44azfit3a4u '@aws-cdk/aws-secretsmanager': 1.193.0_htsmq7wgkec2uefwunmqkzkjia - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 '@aws-cdk/aws-sns-subscriptions': 1.193.0_7kzgzmrjzvracynar4gtrolizi - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 transitivePeerDependencies: @@ -2180,8 +2180,8 @@ packages: '@aws-cdk/aws-s3': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq '@aws-cdk/aws-s3-assets': 1.193.0_fl7hneqefdk7a7rv75sucgtmwm '@aws-cdk/aws-signer': 1.193.0_h5z4mbysj2d57xhjje65guwzkq - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte '@aws-cdk/cx-api': 1.193.0 '@aws-cdk/region-info': 1.193.0 @@ -2329,8 +2329,8 @@ packages: '@aws-cdk/aws-kms': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/aws-lambda': 1.193.0_hjndse4uvjoh2z652h4gibqbfa '@aws-cdk/aws-s3': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 transitivePeerDependencies: @@ -2431,8 +2431,8 @@ packages: '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq '@aws-cdk/aws-kms': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/aws-lambda': 1.193.0_hjndse4uvjoh2z652h4gibqbfa - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 + '@aws-cdk/aws-sqs': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 transitivePeerDependencies: @@ -2440,7 +2440,7 @@ packages: - '@aws-cdk/cx-api' dev: false - /@aws-cdk/aws-sns/1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq: + /@aws-cdk/aws-sns/1.193.0_t7vgobjyz5zbtb4x3hca7evtr4: resolution: {integrity: sha512-VMmVtBzStw6sLmBC/pjMaaX6Ol3mCWgJJdoW7wg9TGzkrkuxFrZiH4KeF05zkaNXi33JAvosPeVjckslCTdgUQ==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -2453,15 +2453,11 @@ packages: '@aws-cdk/aws-codestarnotifications': 1.193.0_h5z4mbysj2d57xhjje65guwzkq '@aws-cdk/aws-events': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq - '@aws-cdk/aws-kms': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu - '@aws-cdk/aws-sqs': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 - transitivePeerDependencies: - - '@aws-cdk/cx-api' dev: false - /@aws-cdk/aws-sqs/1.193.0_wv5he25vlfitynxjm2o4t3p5vu: + /@aws-cdk/aws-sqs/1.193.0_hwrqi6tv767pe2ottnl3pwcnqm: resolution: {integrity: sha512-3roFaibYxBNW+txcHmNOeYhfg1pGWP7edv3E6S2HAQji7kczOWSNxFoVtoBrgKOzoMD+UQWR7V0Jfg2JIfk/EA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -2471,11 +2467,8 @@ packages: dependencies: '@aws-cdk/aws-cloudwatch': 1.193.0_hwrqi6tv767pe2ottnl3pwcnqm '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq - '@aws-cdk/aws-kms': 1.193.0_wv5he25vlfitynxjm2o4t3p5vu '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 - transitivePeerDependencies: - - '@aws-cdk/cx-api' dev: false /@aws-cdk/aws-ssm/1.193.0_wv5he25vlfitynxjm2o4t3p5vu: @@ -2594,7 +2587,7 @@ packages: '@aws-cdk/aws-iam': 1.193.0_h5z4mbysj2d57xhjje65guwzkq '@aws-cdk/aws-lambda': 1.193.0_hjndse4uvjoh2z652h4gibqbfa '@aws-cdk/aws-logs': 1.193.0_fl7hneqefdk7a7rv75sucgtmwm - '@aws-cdk/aws-sns': 1.193.0_s6ljqdn2iypvkcrzn7mbvz6gaq + '@aws-cdk/aws-sns': 1.193.0_t7vgobjyz5zbtb4x3hca7evtr4 '@aws-cdk/core': 1.193.0_jfsf2uupw3z2wi6nn7gnreoyte constructs: 3.4.251 transitivePeerDependencies: diff --git a/packages/framework-core/src/read-model-schema-migrator.ts b/packages/framework-core/src/read-model-schema-migrator.ts index 92971c943..6bf643424 100644 --- a/packages/framework-core/src/read-model-schema-migrator.ts +++ b/packages/framework-core/src/read-model-schema-migrator.ts @@ -3,12 +3,37 @@ import { InvalidVersionError, SchemaMigrationMetadata, ReadModelInterface, + FilterFor, + ReadModelListResult, } from '@boostercloud/framework-types' import { getLogger } from '@boostercloud/framework-common-helpers' export class ReadModelSchemaMigrator { + private static readonly LIMIT = 100 + public constructor(private config: BoosterConfig) {} + public async migrateAll(readModelName: string, batchSize = ReadModelSchemaMigrator.LIMIT): Promise { + const filterFor = this.buildFilterForSearchReadModelsToMigrate(readModelName) + let cursor: Record<'id', string> | undefined = undefined + let total = 0 + do { + const toMigrate: ReadModelListResult = await this.searchReadModelsToMigrate( + readModelName, + filterFor, + batchSize, + cursor + ) + cursor = toMigrate.items.length >= batchSize ? toMigrate.cursor : undefined + const migrationPromises = toMigrate.items.map((item) => this.applyAllMigrations(item, readModelName)) + const migratedReadModels = await Promise.all(migrationPromises) + const persistPromises = migratedReadModels.map((readModel) => this.persistReadModel(readModel, readModelName)) + await Promise.all(persistPromises) + total += toMigrate.count ?? 0 + } while (cursor) + return total + } + public async migrate( readModel: TMigratableReadModel, readModelName: string @@ -95,4 +120,60 @@ export class ReadModelSchemaMigrator { private static readModelSchemaVersion(readModel: ReadModelInterface): number { return readModel.boosterMetadata?.schemaVersion ?? 1 } + + private buildFilterForSearchReadModelsToMigrate(readModelName: string): FilterFor { + const expectedVersion = this.config.currentVersionFor(readModelName) + return { + or: [ + { + boosterMetadata: { + schemaVersion: { + lt: expectedVersion, + }, + }, + }, + { + boosterMetadata: { + schemaVersion: { + isDefined: false, + }, + }, + }, + ], + } + } + + private async searchReadModelsToMigrate( + readModelName: string, + filterFor: FilterFor, + limit: number, + cursor: undefined | Record<'id', string> + ): Promise> { + return (await this.config.provider.readModels.search( + this.config, + readModelName, + filterFor, + {}, + limit, + cursor, + true + )) as ReadModelListResult + } + + private persistReadModel(newReadModel: ReadModelInterface, readModelName: string): Promise { + const logger = getLogger(this.config, 'ReadModelSchemaMigrator#persistReadModel') + if (!(newReadModel && newReadModel.boosterMetadata)) { + throw new Error(`Error migrating ReadModel: ${newReadModel}`) + } + const currentReadModelVersion: number = newReadModel?.boosterMetadata?.version ?? 0 + const schemaVersion: number = + newReadModel?.boosterMetadata?.schemaVersion ?? this.config.currentVersionFor(readModelName) + newReadModel.boosterMetadata = { + ...newReadModel?.boosterMetadata, + version: currentReadModelVersion + 1, + schemaVersion: schemaVersion, + } + logger.debug('Storing new version of read model', newReadModel) + return this.config.provider.readModels.store(this.config, readModelName, newReadModel, currentReadModelVersion) + } } diff --git a/packages/framework-core/test/read-model-schema-migrator.test.ts b/packages/framework-core/test/read-model-schema-migrator.test.ts index 202492d70..5b1923f55 100644 --- a/packages/framework-core/test/read-model-schema-migrator.test.ts +++ b/packages/framework-core/test/read-model-schema-migrator.test.ts @@ -2,8 +2,10 @@ /* eslint-disable @typescript-eslint/no-magic-numbers */ import { expect } from './expect' -import { BoosterConfig, SchemaMigrationMetadata, ReadModelInterface, UUID } from '@boostercloud/framework-types' +import { BoosterConfig, ReadModelInterface, SchemaMigrationMetadata, UUID } from '@boostercloud/framework-types' import { ReadModelSchemaMigrator } from '../src/read-model-schema-migrator' +import 'mocha' +import { fake, replaceGetter } from 'sinon' class TestConceptV1 { public constructor(readonly id: UUID, readonly field1: string) {} @@ -44,10 +46,11 @@ describe('ReadModelSchemaMigrator', () => { toVersion: 3, }) const config = new BoosterConfig('test') - config.schemaMigrations['TestConcept'] = migrations - const migrator = new ReadModelSchemaMigrator(config) describe('migrate', async () => { + config.schemaMigrations['TestConcept'] = migrations + const migrator = new ReadModelSchemaMigrator(config) + it('throws when the schemaVersion of the concept to migrate is lower than 1', async () => { const toMigrate: ReadModelInterface = { id: 'id', @@ -114,4 +117,36 @@ describe('ReadModelSchemaMigrator', () => { expect(got).to.be.deep.equal(expected) }) }) + + describe('migrateAll', () => { + it('returns 0 when there are not ReadModels with schemaVersion less than expected', async () => { + const config = new BoosterConfig('test') + replaceGetter(config, 'provider', () => { + return { + readModels: { + search: fake.returns({ items: [] }), + }, + } as any + }) + const migrator = new ReadModelSchemaMigrator(config) + const number = await migrator.migrateAll('TestConcept') + expect(number).to.be.deep.equal(0) + }) + + it('returns ReadModels with schemaVersion less than expected', async () => { + const config = new BoosterConfig('test') + replaceGetter(config, 'provider', () => { + return { + readModels: { + search: fake.returns({ items: [{ id: 1 }, { id: 2 }, { id: 3 }], count: 3, cursor: { id: 10 } }), + store: fake.resolves(''), + }, + } as any + }) + config.schemaMigrations['TestConcept'] = migrations + const migrator = new ReadModelSchemaMigrator(config) + const number = await migrator.migrateAll('TestConcept') + expect(number).to.be.deep.equal(3) + }) + }) }) diff --git a/packages/framework-integration-tests/src/commands/migrate-all-read-model.ts b/packages/framework-integration-tests/src/commands/migrate-all-read-model.ts new file mode 100644 index 000000000..f74d77bfc --- /dev/null +++ b/packages/framework-integration-tests/src/commands/migrate-all-read-model.ts @@ -0,0 +1,16 @@ +import { Booster, Command } from '@boostercloud/framework-core' +import { Register } from '@boostercloud/framework-types' +import { ReadModelSchemaMigrator } from '@boostercloud/framework-core/dist/read-model-schema-migrator' + +@Command({ + authorize: 'all', +}) +export class MigrateAllReadModel { + public constructor(readonly readModelName: string) {} + + public static async handle(command: MigrateAllReadModel, register: Register): Promise { + const readModelSchemaMigrator = new ReadModelSchemaMigrator(Booster.config) + const result = await readModelSchemaMigrator.migrateAll(command.readModelName) + return `Migrated ${result} ${command.readModelName}` + } +} diff --git a/packages/framework-integration-tests/src/migrations/read-models/CartReadModel/migrations.ts b/packages/framework-integration-tests/src/migrations/read-models/CartReadModel/migrations.ts new file mode 100644 index 000000000..8e5852078 --- /dev/null +++ b/packages/framework-integration-tests/src/migrations/read-models/CartReadModel/migrations.ts @@ -0,0 +1,11 @@ +import { CartReadModelV1, CartReadModelV2 } from './schema-versions' +import { SchemaMigration, ToVersion } from '@boostercloud/framework-core' +import { CartReadModel } from '../../../read-models/cart-read-model' + +@SchemaMigration(CartReadModel) +export class CartReadModelMigration { + @ToVersion(2, { fromSchema: CartReadModelV1, toSchema: CartReadModelV2 }) + public async splitDescriptionFieldIntoShortAndLong(old: CartReadModelV1): Promise { + return new CartReadModelV2(old.id, old.cartItems, 0, old.shippingAddress, old.payment, old.cartItemsIds) + } +} diff --git a/packages/framework-integration-tests/src/migrations/read-models/CartReadModel/schema-versions.ts b/packages/framework-integration-tests/src/migrations/read-models/CartReadModel/schema-versions.ts new file mode 100644 index 000000000..97fadfb91 --- /dev/null +++ b/packages/framework-integration-tests/src/migrations/read-models/CartReadModel/schema-versions.ts @@ -0,0 +1,18 @@ +import { UUID } from '@boostercloud/framework-types' +import { CartReadModel } from '../../../read-models/cart-read-model' +import { CartItem } from '../../../common/cart-item' +import { Address } from '../../../common/address' +import { Payment } from '../../../entities/payment' + +export class CartReadModelV1 { + public constructor( + readonly id: UUID, + readonly cartItems: Array, + public shippingAddress?: Address, + public payment?: Payment, + public cartItemsIds?: Array + ) {} +} + +// Current version +export class CartReadModelV2 extends CartReadModel {} diff --git a/website/docs/10_going-deeper/data-migrations.md b/website/docs/10_going-deeper/data-migrations.md index 2a393acc2..813fbae72 100644 --- a/website/docs/10_going-deeper/data-migrations.md +++ b/website/docs/10_going-deeper/data-migrations.md @@ -96,6 +96,37 @@ export class ProductMigration { In this example, the `changeNameFieldToDisplayName` function updates the `Product` entity from version 1 to version 2 by renaming the `name` field to `displayName`. Then, `addNewField` function updates the `Product` entity from version 2 to version 3 by adding a new field called `newField` to the entity's schema. Notice that at this point, your database could have snapshots set as v1, v2, or v3, so while it might be tempting to redefine the original migration to keep a single 1-to-3 migration, it's usually a good idea to keep the intermediate steps. This way Booster will be able to handle any scenario. +### Migrating Multiple ReadModels at Once with "migrateAll" + +ReadModelSchemaMigrator includes the method "migrateAll" that enables you to migrate all ReadModels of a specific type in a single step. + +To use the "migrateAll" method, simply pass the name of the ReadModel type you wish to migrate. The system will automatically migrate all ReadModels of that type that have a version less than the expected one. + +In addition, the "migrateAll" method of Booster persists the changes made to the database. This ensures that the changes in the structure of the ReadModels are permanently stored in the database. + +Furthermore, the "migrateAll" method also allows you to specify a second parameter that defines the size of the blocks in which the ReadModels are processed. If a number is passed, for example 10, the ReadModels will be read, migrated, and saved in blocks of 10. + +Example: + +```typescript +import { Booster, Command } from '@boostercloud/framework-core' +import { Register } from '@boostercloud/framework-types' +import { ReadModelSchemaMigrator } from '@boostercloud/framework-core/dist/read-model-schema-migrator' + +@Command({ + authorize: 'all', +}) +export class MigrateAllReadModel { + public constructor(readonly readModelName: string) {} + + public static async handle(command: MigrateAllReadModel, register: Register): Promise { + const readModelSchemaMigrator = new ReadModelSchemaMigrator(Booster.config) + const result = await readModelSchemaMigrator.migrateAll(command.readModelName) + return `Migrated ${result} ${command.readModelName}` + } +} +``` + ## Data migrations Data migrations can be seen as background processes that can actively update the values of existing entities and read models in the database. They can be useful to perform data migrations that cannot be handled with schema migrations, for example when you need to update the values exposed by the GraphQL API, or to initialize new read models that are projections of previously existing entities.