sonamu 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.swcrc.project-default +18 -0
- package/bin/cli.js +24 -0
- package/dist/ai/agents/agent.d.ts +11 -0
- package/dist/ai/agents/agent.d.ts.map +1 -0
- package/dist/ai/agents/agent.js +65 -0
- package/dist/ai/agents/index.d.ts +3 -0
- package/dist/ai/agents/index.d.ts.map +1 -0
- package/dist/ai/agents/index.js +4 -0
- package/dist/ai/agents/types.d.ts +43 -0
- package/dist/ai/agents/types.d.ts.map +1 -0
- package/dist/ai/agents/types.js +3 -0
- package/dist/ai/index.d.ts +2 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +3 -0
- package/dist/ai/providers/rtzr/api.d.ts +22 -0
- package/dist/ai/providers/rtzr/api.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/api.js +28 -0
- package/dist/ai/providers/rtzr/error.d.ts +18 -0
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/error.js +29 -0
- package/dist/ai/providers/rtzr/index.d.ts +5 -0
- package/dist/ai/providers/rtzr/index.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/index.js +6 -0
- package/dist/ai/providers/rtzr/model.d.ts +52 -0
- package/dist/ai/providers/rtzr/model.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/model.js +137 -0
- package/dist/ai/providers/rtzr/options.d.ts +7 -0
- package/dist/ai/providers/rtzr/options.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/options.js +47 -0
- package/dist/ai/providers/rtzr/provider.d.ts +18 -0
- package/dist/ai/providers/rtzr/provider.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/provider.js +54 -0
- package/dist/ai/providers/rtzr/utils.d.ts +19 -0
- package/dist/ai/providers/rtzr/utils.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/utils.js +88 -0
- package/dist/api/base-frame.d.ts +2 -2
- package/dist/api/base-frame.d.ts.map +1 -1
- package/dist/api/base-frame.js +2 -1
- package/dist/api/caster.d.ts.map +1 -1
- package/dist/api/caster.js +6 -1
- package/dist/api/code-converters.d.ts +58 -14
- package/dist/api/code-converters.d.ts.map +1 -1
- package/dist/api/code-converters.js +178 -409
- package/dist/api/config.d.ts +27 -13
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +19 -26
- package/dist/api/context.d.ts +4 -3
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +1 -1
- package/dist/api/decorators.d.ts +20 -6
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +111 -18
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +3 -3
- package/dist/api/sonamu.d.ts +7 -7
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +83 -51
- package/dist/api/validator.d.ts +6 -0
- package/dist/api/validator.d.ts.map +1 -0
- package/dist/api/validator.js +81 -0
- package/dist/bin/build-config.d.ts +5 -1
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +5 -2
- package/dist/bin/cli.js +165 -64
- package/dist/bin/loader-register.d.ts +2 -0
- package/dist/bin/loader-register.d.ts.map +1 -0
- package/dist/bin/loader-register.js +34 -0
- package/dist/database/_batch_update.d.ts +5 -3
- package/dist/database/_batch_update.d.ts.map +1 -1
- package/dist/database/_batch_update.js +30 -13
- package/dist/database/base-model.d.ts +96 -10
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +232 -89
- package/dist/database/base-model.types.d.ts +93 -0
- package/dist/database/base-model.types.d.ts.map +1 -0
- package/dist/database/base-model.types.js +10 -0
- package/dist/database/code-generator.d.ts +1 -1
- package/dist/database/code-generator.d.ts.map +1 -1
- package/dist/database/code-generator.js +11 -10
- package/dist/database/db.d.ts +5 -6
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +22 -25
- package/dist/database/puri-subset.test-d.js +81 -0
- package/dist/database/puri-subset.types.d.ts +123 -0
- package/dist/database/puri-subset.types.d.ts.map +1 -0
- package/dist/database/puri-subset.types.js +16 -0
- package/dist/database/puri-wrapper.d.ts +13 -11
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +2 -2
- package/dist/database/puri.d.ts +25 -14
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +83 -21
- package/dist/database/puri.types.d.ts +21 -7
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +4 -1
- package/dist/database/transaction-context.d.ts +1 -1
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/transaction-context.js +1 -1
- package/dist/database/upsert-builder.d.ts +9 -3
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +227 -78
- package/dist/entity/entity-manager.d.ts +165 -2
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +26 -10
- package/dist/entity/entity.d.ts +5 -3
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +153 -54
- package/dist/exceptions/error-handler.d.ts +1 -1
- package/dist/exceptions/error-handler.d.ts.map +1 -1
- package/dist/exceptions/error-handler.js +1 -1
- package/dist/exceptions/so-exceptions.d.ts +1 -1
- package/dist/exceptions/so-exceptions.d.ts.map +1 -1
- package/dist/exceptions/so-exceptions.js +1 -1
- package/dist/file-storage/driver.d.ts +1 -1
- package/dist/file-storage/driver.d.ts.map +1 -1
- package/dist/file-storage/driver.js +1 -1
- package/dist/file-storage/file-storage.js +2 -2
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -13
- package/dist/migration/code-generation.d.ts +1 -1
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +123 -67
- package/dist/migration/migration-set.d.ts +2 -10
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +67 -218
- package/dist/migration/migrator.d.ts +24 -73
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +121 -301
- package/dist/migration/postgresql-schema-reader.d.ts +51 -0
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -0
- package/dist/migration/postgresql-schema-reader.js +245 -0
- package/dist/migration/types.d.ts +6 -38
- package/dist/migration/types.d.ts.map +1 -1
- package/dist/migration/types.js +1 -1
- package/dist/naite/messaging-types.d.ts +43 -0
- package/dist/naite/messaging-types.d.ts.map +1 -0
- package/dist/naite/messaging-types.js +7 -0
- package/dist/naite/naite-reporter.d.ts +41 -0
- package/dist/naite/naite-reporter.d.ts.map +1 -0
- package/dist/naite/naite-reporter.js +102 -0
- package/dist/naite/naite.d.ts +91 -8
- package/dist/naite/naite.d.ts.map +1 -1
- package/dist/naite/naite.js +285 -41
- package/dist/stream/sse.d.ts +2 -2
- package/dist/stream/sse.d.ts.map +1 -1
- package/dist/stream/sse.js +1 -1
- package/dist/syncer/api-parser.d.ts +3 -13
- package/dist/syncer/api-parser.d.ts.map +1 -1
- package/dist/syncer/api-parser.js +67 -56
- package/dist/syncer/checksum.d.ts +2 -2
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +11 -11
- package/dist/syncer/code-generator.d.ts +3 -3
- package/dist/syncer/code-generator.d.ts.map +1 -1
- package/dist/syncer/code-generator.js +37 -17
- package/dist/syncer/entity-operations.d.ts +2 -2
- package/dist/syncer/entity-operations.d.ts.map +1 -1
- package/dist/syncer/entity-operations.js +9 -8
- package/dist/syncer/file-patterns.d.ts +1 -1
- package/dist/syncer/file-patterns.d.ts.map +1 -1
- package/dist/syncer/file-patterns.js +1 -1
- package/dist/syncer/index.d.ts +4 -4
- package/dist/syncer/index.d.ts.map +1 -1
- package/dist/syncer/index.js +5 -5
- package/dist/syncer/module-loader.d.ts +4 -4
- package/dist/syncer/module-loader.d.ts.map +1 -1
- package/dist/syncer/module-loader.js +17 -12
- package/dist/syncer/syncer.d.ts +31 -24
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +92 -45
- package/dist/template/entity-converter.d.ts +1 -1
- package/dist/template/entity-converter.d.ts.map +1 -1
- package/dist/template/entity-converter.js +15 -8
- package/dist/template/helpers.d.ts +2 -2
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +3 -3
- package/dist/template/implementations/entity.template.d.ts +2 -2
- package/dist/template/implementations/entity.template.d.ts.map +1 -1
- package/dist/template/implementations/entity.template.js +4 -5
- package/dist/template/implementations/generated.template.d.ts +2 -3
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +46 -29
- package/dist/template/implementations/generated_http.template.d.ts +2 -3
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +9 -9
- package/dist/template/implementations/generated_sso.template.d.ts +3 -4
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +54 -25
- package/dist/template/implementations/init_types.template.d.ts +2 -2
- package/dist/template/implementations/init_types.template.d.ts.map +1 -1
- package/dist/template/implementations/init_types.template.js +2 -2
- package/dist/template/implementations/model.template.d.ts +2 -2
- package/dist/template/implementations/model.template.d.ts.map +1 -1
- package/dist/template/implementations/model.template.js +47 -37
- package/dist/template/implementations/model_test.template.d.ts +2 -2
- package/dist/template/implementations/model_test.template.d.ts.map +1 -1
- package/dist/template/implementations/model_test.template.js +2 -2
- package/dist/template/implementations/service.template.d.ts +4 -4
- package/dist/template/implementations/service.template.d.ts.map +1 -1
- package/dist/template/implementations/service.template.js +24 -16
- package/dist/template/implementations/view_enums_buttonset.template.d.ts +2 -2
- package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +1 -1
- package/dist/template/implementations/view_enums_buttonset.template.js +1 -1
- package/dist/template/implementations/view_enums_dropdown.template.d.ts +2 -2
- package/dist/template/implementations/view_enums_dropdown.template.d.ts.map +1 -1
- package/dist/template/implementations/view_enums_dropdown.template.js +2 -2
- package/dist/template/implementations/view_enums_select.template.d.ts +2 -2
- package/dist/template/implementations/view_enums_select.template.d.ts.map +1 -1
- package/dist/template/implementations/view_enums_select.template.js +2 -2
- package/dist/template/implementations/view_form.template.d.ts +2 -2
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +4 -4
- package/dist/template/implementations/view_id_all_select.template.d.ts +2 -2
- package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
- package/dist/template/implementations/view_id_all_select.template.js +1 -1
- package/dist/template/implementations/view_id_async_select.template.d.ts +2 -2
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +1 -1
- package/dist/template/implementations/view_id_async_select.template.js +1 -1
- package/dist/template/implementations/view_list.template.d.ts +2 -2
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +29 -19
- package/dist/template/implementations/view_list_columns.template.d.ts +3 -3
- package/dist/template/implementations/view_list_columns.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list_columns.template.js +1 -1
- package/dist/template/implementations/view_search_input.template.d.ts +2 -2
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
- package/dist/template/implementations/view_search_input.template.js +1 -1
- package/dist/template/index.d.ts +4 -2
- package/dist/template/index.d.ts.map +1 -1
- package/dist/template/index.js +5 -3
- package/dist/template/template-manager.d.ts +56 -0
- package/dist/template/template-manager.d.ts.map +1 -0
- package/dist/template/template-manager.js +125 -0
- package/dist/template/template-types.d.ts +16 -0
- package/dist/template/template-types.d.ts.map +1 -0
- package/dist/template/template-types.js +7 -0
- package/dist/template/template.d.ts +12 -2
- package/dist/template/template.d.ts.map +1 -1
- package/dist/template/template.js +19 -6
- package/dist/template/zod-converter.d.ts +40 -7
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +386 -58
- package/dist/testing/_relation-graph.d.ts +1 -1
- package/dist/testing/_relation-graph.d.ts.map +1 -1
- package/dist/testing/_relation-graph.js +12 -3
- package/dist/testing/fixture-manager.d.ts +42 -11
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +338 -236
- package/dist/types/types.d.ts +709 -104
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +309 -52
- package/dist/typings/knex.d.js +2 -2
- package/dist/utils/async-utils.d.ts.map +1 -1
- package/dist/utils/async-utils.js +3 -3
- package/dist/utils/console-util.js +1 -1
- package/dist/utils/controller.d.ts +1 -0
- package/dist/utils/controller.d.ts.map +1 -1
- package/dist/utils/controller.js +4 -1
- package/dist/utils/esm-utils.d.ts +0 -6
- package/dist/utils/esm-utils.d.ts.map +1 -1
- package/dist/utils/esm-utils.js +2 -9
- package/dist/utils/formatter.d.ts +3 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/formatter.js +110 -0
- package/dist/utils/fs-utils.d.ts +1 -1
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +1 -1
- package/dist/utils/lodash-able.d.ts.map +1 -1
- package/dist/utils/lodash-able.js +1 -1
- package/dist/utils/object-utils.d.ts +44 -0
- package/dist/utils/object-utils.d.ts.map +1 -0
- package/dist/utils/object-utils.js +191 -0
- package/dist/utils/path-utils.d.ts +1 -1
- package/dist/utils/path-utils.d.ts.map +1 -1
- package/dist/utils/path-utils.js +3 -3
- package/dist/utils/process-utils.js +1 -1
- package/dist/utils/sql-parser.d.ts +5 -1
- package/dist/utils/sql-parser.d.ts.map +1 -1
- package/dist/utils/sql-parser.js +14 -3
- package/dist/utils/type-utils.d.ts +23 -0
- package/dist/utils/type-utils.d.ts.map +1 -0
- package/dist/utils/type-utils.js +45 -0
- package/dist/utils/utils.d.ts +7 -1
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +44 -5
- package/dist/utils/zod-error.d.ts +1 -1
- package/dist/utils/zod-error.d.ts.map +1 -1
- package/dist/utils/zod-error.js +1 -1
- package/package.json +55 -30
- package/src/ai/agents/agent.ts +87 -0
- package/src/ai/agents/index.ts +2 -0
- package/src/ai/agents/types.ts +47 -0
- package/src/ai/index.ts +1 -0
- package/src/ai/providers/rtzr/api.ts +37 -0
- package/src/ai/providers/rtzr/error.ts +34 -0
- package/src/ai/providers/rtzr/index.ts +4 -0
- package/src/ai/providers/rtzr/model.ts +201 -0
- package/src/ai/providers/rtzr/options.ts +49 -0
- package/src/ai/providers/rtzr/provider.ts +91 -0
- package/src/ai/providers/rtzr/utils.ts +127 -0
- package/src/api/base-frame.ts +4 -2
- package/src/api/caster.ts +17 -23
- package/src/api/code-converters.ts +176 -533
- package/src/api/config.ts +39 -56
- package/src/api/context.ts +7 -18
- package/src/api/decorators.ts +175 -46
- package/src/api/index.ts +2 -2
- package/src/api/sonamu.ts +133 -124
- package/src/api/validator.ts +83 -0
- package/src/bin/build-config.ts +7 -1
- package/src/bin/cli.ts +192 -110
- package/src/bin/loader-register.ts +38 -0
- package/src/database/_batch_update.ts +46 -31
- package/src/database/base-model.ts +390 -182
- package/src/database/base-model.types.ts +155 -0
- package/src/database/code-generator.ts +13 -32
- package/src/database/db.ts +36 -50
- package/src/database/puri-subset.test-d.ts +471 -0
- package/src/database/puri-subset.types.ts +195 -0
- package/src/database/puri-wrapper.ts +58 -67
- package/src/database/puri.ts +182 -126
- package/src/database/puri.types.ts +64 -31
- package/src/database/transaction-context.ts +1 -1
- package/src/database/upsert-builder.ts +261 -132
- package/src/entity/entity-manager.ts +36 -28
- package/src/entity/entity.ts +330 -249
- package/src/exceptions/error-handler.ts +3 -3
- package/src/exceptions/so-exceptions.ts +11 -11
- package/src/file-storage/driver.ts +5 -5
- package/src/file-storage/file-storage.ts +2 -2
- package/src/index.ts +18 -12
- package/src/migration/code-generation.ts +185 -172
- package/src/migration/migration-set.ts +80 -293
- package/src/migration/migrator.ts +182 -425
- package/src/migration/mysql-schema-reader.ts.txt +272 -0
- package/src/migration/postgresql-schema-reader.ts +310 -0
- package/src/migration/types.ts +6 -39
- package/src/naite/messaging-types.ts +51 -0
- package/src/naite/naite-reporter.ts +128 -0
- package/src/naite/naite.ts +378 -33
- package/src/shared/web.shared.ts.txt +20 -24
- package/src/stream/sse.ts +5 -5
- package/src/syncer/api-parser.ts +52 -69
- package/src/syncer/checksum.ts +25 -37
- package/src/syncer/code-generator.ts +58 -62
- package/src/syncer/entity-operations.ts +12 -15
- package/src/syncer/file-patterns.ts +2 -2
- package/src/syncer/index.ts +4 -4
- package/src/syncer/module-loader.ts +28 -25
- package/src/syncer/syncer.ts +155 -162
- package/src/template/entity-converter.ts +18 -27
- package/src/template/helpers.ts +8 -11
- package/src/template/implementations/entity.template.ts +6 -6
- package/src/template/implementations/generated.template.ts +99 -99
- package/src/template/implementations/generated_http.template.ts +21 -54
- package/src/template/implementations/generated_sso.template.ts +78 -65
- package/src/template/implementations/init_types.template.ts +4 -6
- package/src/template/implementations/model.template.ts +47 -38
- package/src/template/implementations/model_test.template.ts +3 -3
- package/src/template/implementations/service.template.ts +56 -80
- package/src/template/implementations/view_enums_buttonset.template.ts +2 -2
- package/src/template/implementations/view_enums_dropdown.template.ts +4 -4
- package/src/template/implementations/view_enums_select.template.ts +3 -3
- package/src/template/implementations/view_form.template.ts +34 -75
- package/src/template/implementations/view_id_all_select.template.ts +2 -2
- package/src/template/implementations/view_id_async_select.template.ts +9 -23
- package/src/template/implementations/view_list.template.ts +54 -95
- package/src/template/implementations/view_list_columns.template.ts +4 -10
- package/src/template/implementations/view_search_input.template.ts +2 -2
- package/src/template/index.ts +4 -2
- package/src/template/template-manager.ts +166 -0
- package/src/template/template-types.ts +16 -0
- package/src/template/template.ts +29 -10
- package/src/template/zod-converter.ts +459 -101
- package/src/testing/_relation-graph.ts +18 -11
- package/src/testing/fixture-manager.ts +468 -362
- package/src/types/types.ts +516 -248
- package/src/typings/knex.d.ts +7 -9
- package/src/utils/async-utils.ts +8 -12
- package/src/utils/console-util.ts +1 -1
- package/src/utils/controller.ts +3 -0
- package/src/utils/esm-utils.ts +8 -18
- package/src/utils/formatter.ts +109 -0
- package/src/utils/fs-utils.ts +1 -1
- package/src/utils/lodash-able.ts +1 -4
- package/src/utils/object-utils.ts +217 -0
- package/src/utils/path-utils.ts +3 -6
- package/src/utils/process-utils.ts +1 -1
- package/src/utils/sql-parser.ts +23 -5
- package/src/utils/type-utils.ts +83 -0
- package/src/utils/utils.ts +58 -9
- package/src/utils/zod-error.ts +3 -3
- package/dist/bin/cli-wrapper.d.ts +0 -3
- package/dist/bin/cli-wrapper.d.ts.map +0 -1
- package/dist/bin/cli-wrapper.js +0 -72
- package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts +0 -2
- package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts.map +0 -1
- package/dist/database/knex-plugins/knex-on-duplicate-update.js +0 -39
- package/dist/entity/entity-utils.d.ts +0 -61
- package/dist/entity/entity-utils.d.ts.map +0 -1
- package/dist/entity/entity-utils.js +0 -210
- package/src/bin/cli-wrapper.ts +0 -82
- package/src/database/knex-plugins/knex-on-duplicate-update.ts +0 -45
- package/src/entity/entity-utils.ts +0 -291
|
@@ -1,78 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import knex, { Knex } from "knex";
|
|
1
|
+
import assert from "assert";
|
|
3
2
|
import chalk from "chalk";
|
|
4
|
-
import { DateTime } from "luxon";
|
|
5
3
|
import { mkdir, readdir, unlink, writeFile } from "fs/promises";
|
|
6
|
-
import {
|
|
7
|
-
import prompts from "prompts";
|
|
8
|
-
import { execSync } from "child_process";
|
|
4
|
+
import knex, { type Knex } from "knex";
|
|
9
5
|
import path from "path";
|
|
10
|
-
import {
|
|
11
|
-
import { EntityManager } from "../entity/entity-manager";
|
|
6
|
+
import { group, sum, unique } from "radashi";
|
|
12
7
|
import { Sonamu } from "../api";
|
|
8
|
+
import { DB, type SonamuDBConfig } from "../database/db";
|
|
9
|
+
import { EntityManager } from "../entity/entity-manager";
|
|
13
10
|
import { ServiceUnavailableException } from "../exceptions/so-exceptions";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
11
|
+
import { Naite } from "../naite/naite";
|
|
12
|
+
import type { GenMigrationCode, MigrationSet } from "../types/types";
|
|
13
|
+
import { isTest } from "../utils/controller";
|
|
14
|
+
import { exists } from "../utils/fs-utils";
|
|
15
|
+
import { generateAlterCode, generateCreateCode } from "./code-generation";
|
|
18
16
|
import { getMigrationSetFromEntity } from "./migration-set";
|
|
17
|
+
import { PostgreSQLSchemaReader } from "./postgresql-schema-reader";
|
|
18
|
+
import type { ConnString, MigrationCode, MigrationStatus } from "./types";
|
|
19
19
|
|
|
20
|
-
type
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
export type MigrationResult = {
|
|
21
|
+
connKey: string;
|
|
22
|
+
batchNo: number;
|
|
23
|
+
applied: string[];
|
|
24
|
+
}[];
|
|
24
25
|
|
|
25
26
|
export class Migrator {
|
|
26
|
-
targets: {
|
|
27
|
-
compare?: Knex;
|
|
28
|
-
pending: Knex;
|
|
29
|
-
shadow: Knex;
|
|
30
|
-
apply: Knex[];
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
constructor(private readonly options: MigratorOptions) {
|
|
34
|
-
const { dbConfig } = Sonamu;
|
|
35
|
-
|
|
36
|
-
if (this.options.mode === "dev") {
|
|
37
|
-
const devDB = knex(dbConfig.development_master);
|
|
38
|
-
const testDB = knex(dbConfig.test);
|
|
39
|
-
const fixtureLocalDB = knex(dbConfig.fixture_local);
|
|
40
|
-
|
|
41
|
-
const applyDBs = [devDB, testDB, fixtureLocalDB];
|
|
42
|
-
if (
|
|
43
|
-
(dbConfig.fixture_local.connection as Knex.MySql2ConnectionConfig)
|
|
44
|
-
.host !==
|
|
45
|
-
(dbConfig.fixture_remote.connection as Knex.MySql2ConnectionConfig)
|
|
46
|
-
.host ||
|
|
47
|
-
(dbConfig.fixture_local.connection as Knex.MySql2ConnectionConfig)
|
|
48
|
-
.database !==
|
|
49
|
-
(dbConfig.fixture_remote.connection as Knex.MySql2ConnectionConfig)
|
|
50
|
-
.database
|
|
51
|
-
) {
|
|
52
|
-
const fixtureRemoteDB = knex(dbConfig.fixture_remote);
|
|
53
|
-
applyDBs.push(fixtureRemoteDB);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
this.targets = {
|
|
57
|
-
compare: devDB,
|
|
58
|
-
pending: devDB,
|
|
59
|
-
shadow: testDB,
|
|
60
|
-
apply: applyDBs,
|
|
61
|
-
};
|
|
62
|
-
} else if (this.options.mode === "deploy") {
|
|
63
|
-
const productionDB = knex(dbConfig.production_master);
|
|
64
|
-
const testDB = knex(dbConfig.test);
|
|
65
|
-
|
|
66
|
-
this.targets = {
|
|
67
|
-
pending: productionDB,
|
|
68
|
-
shadow: testDB,
|
|
69
|
-
apply: [productionDB],
|
|
70
|
-
};
|
|
71
|
-
} else {
|
|
72
|
-
throw new Error(`잘못된 모드 ${this.options.mode} 입력`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
27
|
private async getMigrationCodes(): Promise<MigrationCode[]> {
|
|
77
28
|
const srcMigrationsDir = path.join(Sonamu.apiRootPath, "src", "migrations"); // 이건 환경에 관계없이 항상 src에서 찾아야 해요.
|
|
78
29
|
|
|
@@ -90,6 +41,7 @@ export class Migrator {
|
|
|
90
41
|
}))
|
|
91
42
|
.sort((a, b) => (a.name < b.name ? 1 : -1)); // 이름 내림차순 정렬(최신순)
|
|
92
43
|
|
|
44
|
+
Naite.t("migrator:getMigrationCodes:results", codes);
|
|
93
45
|
return codes;
|
|
94
46
|
}
|
|
95
47
|
|
|
@@ -104,11 +56,14 @@ export class Migrator {
|
|
|
104
56
|
*/
|
|
105
57
|
async getStatus(): Promise<MigrationStatus> {
|
|
106
58
|
const codes = await this.getMigrationCodes();
|
|
59
|
+
Naite.t("migrator:getStatus:codes", codes);
|
|
107
60
|
|
|
108
61
|
const connKeys = Object.keys(Sonamu.dbConfig).filter(
|
|
109
|
-
(key) => key.endsWith("_slave") === false
|
|
62
|
+
(key) => key.endsWith("_slave") === false,
|
|
110
63
|
) as (keyof typeof Sonamu.dbConfig)[];
|
|
111
64
|
|
|
65
|
+
let migrationStatusError: string | undefined;
|
|
66
|
+
|
|
112
67
|
const statuses = await Promise.all(
|
|
113
68
|
connKeys.map(async (connKey) => {
|
|
114
69
|
const knexOptions = Sonamu.dbConfig[connKey];
|
|
@@ -120,55 +75,58 @@ export class Migrator {
|
|
|
120
75
|
} catch (err) {
|
|
121
76
|
console.warn(
|
|
122
77
|
chalk.yellow(
|
|
123
|
-
`${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n
|
|
124
|
-
)
|
|
78
|
+
`${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`,
|
|
79
|
+
),
|
|
125
80
|
);
|
|
126
|
-
|
|
81
|
+
migrationStatusError = err instanceof Error ? err.message : String(err);
|
|
82
|
+
return "error";
|
|
127
83
|
}
|
|
128
84
|
})();
|
|
129
|
-
const pending = await (async () => {
|
|
85
|
+
const pending: string[] = await (async () => {
|
|
130
86
|
try {
|
|
131
87
|
const [, fdList] = await tConn.migrate.list();
|
|
132
|
-
return fdList.map((fd: { file: string }) =>
|
|
133
|
-
fd.file.replace(".ts", "")
|
|
134
|
-
);
|
|
88
|
+
return fdList.map((fd: { file: string }) => fd.file.replace(".ts", ""));
|
|
135
89
|
} catch (err) {
|
|
90
|
+
migrationStatusError = err instanceof Error ? err.message : String(err);
|
|
136
91
|
return [];
|
|
137
92
|
}
|
|
138
93
|
})();
|
|
139
94
|
const currentVersion = await (async () => {
|
|
140
95
|
try {
|
|
141
96
|
return await tConn.migrate.currentVersion();
|
|
142
|
-
} catch (
|
|
97
|
+
} catch (_err) {
|
|
98
|
+
migrationStatusError = _err instanceof Error ? _err.message : String(_err);
|
|
143
99
|
return "error";
|
|
144
100
|
}
|
|
145
101
|
})();
|
|
102
|
+
Naite.t("migrator:getStatus:status", status);
|
|
146
103
|
|
|
147
|
-
const connection =
|
|
148
|
-
knexOptions.connection as Knex.MySql2ConnectionConfig;
|
|
104
|
+
const connection = knexOptions.connection as Knex.PgConnectionConfig;
|
|
149
105
|
|
|
150
106
|
await tConn.destroy();
|
|
151
107
|
|
|
152
108
|
return {
|
|
153
109
|
name: connKey.replace("_master", ""),
|
|
154
110
|
connKey,
|
|
155
|
-
connString: `
|
|
111
|
+
connString: `pg://${connection.user ?? ""}@${connection.host}:${
|
|
156
112
|
connection.port
|
|
157
113
|
}/${connection.database}` as ConnString,
|
|
158
114
|
currentVersion,
|
|
159
|
-
status,
|
|
115
|
+
status: status as number | "error",
|
|
160
116
|
pending,
|
|
161
117
|
};
|
|
162
|
-
})
|
|
118
|
+
}),
|
|
163
119
|
);
|
|
164
120
|
|
|
121
|
+
Naite.t("migrator:getStatus:conns", statuses);
|
|
122
|
+
|
|
165
123
|
const preparedCodes: GenMigrationCode[] = await (async () => {
|
|
166
124
|
const status0conn = statuses.find((status) => status.status === 0);
|
|
167
125
|
if (status0conn === undefined) {
|
|
168
126
|
console.warn(
|
|
169
127
|
chalk.yellow(
|
|
170
|
-
`While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases
|
|
171
|
-
)
|
|
128
|
+
`While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases.`,
|
|
129
|
+
),
|
|
172
130
|
);
|
|
173
131
|
return [];
|
|
174
132
|
}
|
|
@@ -181,20 +139,14 @@ export class Migrator {
|
|
|
181
139
|
return genCodes;
|
|
182
140
|
})();
|
|
183
141
|
|
|
142
|
+
Naite.t("migrator:getStatus:preparedCodes", preparedCodes);
|
|
143
|
+
|
|
184
144
|
return {
|
|
185
145
|
conns: statuses,
|
|
186
146
|
codes,
|
|
187
147
|
preparedCodes,
|
|
148
|
+
error: migrationStatusError,
|
|
188
149
|
};
|
|
189
|
-
/*
|
|
190
|
-
DB 마이그레이션 상태 확인
|
|
191
|
-
1. 전체 DB설정에 대해서 현재 마이그레이션 상태 확인
|
|
192
|
-
- connKey: string
|
|
193
|
-
- status: number
|
|
194
|
-
- currentVersion: string
|
|
195
|
-
- list: { file: string; directory: string }[]
|
|
196
|
-
|
|
197
|
-
*/
|
|
198
150
|
}
|
|
199
151
|
|
|
200
152
|
/**
|
|
@@ -209,16 +161,13 @@ export class Migrator {
|
|
|
209
161
|
*/
|
|
210
162
|
async runAction(
|
|
211
163
|
action: "apply" | "rollback",
|
|
212
|
-
targets: (keyof SonamuDBConfig)[]
|
|
213
|
-
): Promise<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
applied: string[];
|
|
218
|
-
}[]
|
|
219
|
-
> {
|
|
164
|
+
targets: (keyof SonamuDBConfig)[],
|
|
165
|
+
): Promise<MigrationResult> {
|
|
166
|
+
Naite.t("migrator:runAction:action", action);
|
|
167
|
+
Naite.t("migrator:runAction:targets", targets);
|
|
168
|
+
|
|
220
169
|
// get uniq knex configs
|
|
221
|
-
const configs =
|
|
170
|
+
const configs = unique(
|
|
222
171
|
targets
|
|
223
172
|
.map((target) => ({
|
|
224
173
|
connKey: target,
|
|
@@ -226,9 +175,9 @@ export class Migrator {
|
|
|
226
175
|
}))
|
|
227
176
|
.filter((c) => c.options !== undefined),
|
|
228
177
|
({ options }) =>
|
|
229
|
-
`${(options.connection as Knex.
|
|
230
|
-
(options.connection as Knex.
|
|
231
|
-
}/${(options.connection as Knex.
|
|
178
|
+
`${(options.connection as Knex.PgConnectionConfig).host}:${
|
|
179
|
+
(options.connection as Knex.PgConnectionConfig).port ?? 5432
|
|
180
|
+
}/${(options.connection as Knex.PgConnectionConfig).database}`,
|
|
232
181
|
);
|
|
233
182
|
|
|
234
183
|
// get connections
|
|
@@ -236,7 +185,7 @@ export class Migrator {
|
|
|
236
185
|
configs.map(async (config) => ({
|
|
237
186
|
connKey: config.connKey,
|
|
238
187
|
knex: knex(config.options),
|
|
239
|
-
}))
|
|
188
|
+
})),
|
|
240
189
|
);
|
|
241
190
|
|
|
242
191
|
// action
|
|
@@ -249,9 +198,9 @@ export class Migrator {
|
|
|
249
198
|
return {
|
|
250
199
|
connKey,
|
|
251
200
|
batchNo,
|
|
252
|
-
applied,
|
|
201
|
+
applied, // 이번 latest 호출로 인해 "up"이 적용된 마이그레이션 이름(e.g. "20251124233557_create__companies.ts")들의 배열입니다. 참고: https://github.com/knex/knex/blob/01b177c485d696f1b72858dee728ba143c4fad76/lib/migrations/migrate/Migrator.js#L560
|
|
253
202
|
};
|
|
254
|
-
})
|
|
203
|
+
}),
|
|
255
204
|
);
|
|
256
205
|
case "rollback":
|
|
257
206
|
return Promise.all(
|
|
@@ -260,9 +209,9 @@ export class Migrator {
|
|
|
260
209
|
return {
|
|
261
210
|
connKey,
|
|
262
211
|
batchNo,
|
|
263
|
-
applied,
|
|
212
|
+
applied, // 이번 rollback 호출로 인해 "down"이 적용된(=롤백된) 마이그레이션 이름(e.g. "20251124233557_create__companies.ts")들의 배열입니다. 참고: https://github.com/knex/knex/blob/01b177c485d696f1b72858dee728ba143c4fad76/lib/migrations/migrate/Migrator.js#L611
|
|
264
213
|
};
|
|
265
|
-
})
|
|
214
|
+
}),
|
|
266
215
|
);
|
|
267
216
|
}
|
|
268
217
|
})();
|
|
@@ -271,12 +220,32 @@ export class Migrator {
|
|
|
271
220
|
await Promise.all(
|
|
272
221
|
conns.map(({ knex }) => {
|
|
273
222
|
return knex.destroy();
|
|
274
|
-
})
|
|
223
|
+
}),
|
|
275
224
|
);
|
|
276
225
|
|
|
226
|
+
Naite.t("migrator:runAction:result", result);
|
|
227
|
+
|
|
277
228
|
return result;
|
|
278
229
|
}
|
|
279
230
|
|
|
231
|
+
/**
|
|
232
|
+
* 삭제 가능한 마이그레이션 코드 파일을 검증합니다.
|
|
233
|
+
*
|
|
234
|
+
* @param conns 마이그레이션 상태 배열
|
|
235
|
+
* @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
|
|
236
|
+
* @returns 삭제 가능 여부 및 적용된 마이그레이션 코드 파일 이름
|
|
237
|
+
*/
|
|
238
|
+
validateDeletable(conns: MigrationStatus["conns"], codeNames: string[]) {
|
|
239
|
+
const appliedCodes = codeNames.filter((codeName) =>
|
|
240
|
+
conns.some((conn) => conn.pending.includes(codeName) === false),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
canDelete: appliedCodes.length === 0,
|
|
245
|
+
appliedCodes,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
280
249
|
/**
|
|
281
250
|
* 마이그레이션 코드 파일을 삭제합니다.
|
|
282
251
|
*
|
|
@@ -287,33 +256,38 @@ export class Migrator {
|
|
|
287
256
|
*/
|
|
288
257
|
async delCodes(codeNames: string[]): Promise<number> {
|
|
289
258
|
const { conns } = await this.getStatus();
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return codeNames.some(
|
|
293
|
-
(codeName) => conn.pending.includes(codeName) === false
|
|
294
|
-
);
|
|
295
|
-
})
|
|
296
|
-
) {
|
|
259
|
+
const { canDelete, appliedCodes } = this.validateDeletable(conns, codeNames);
|
|
260
|
+
if (!canDelete) {
|
|
297
261
|
throw new Error(
|
|
298
|
-
|
|
262
|
+
`You cannot delete a migration file if there is already applied. Applied codes: ${appliedCodes.join(", ")}`,
|
|
299
263
|
);
|
|
300
264
|
}
|
|
301
265
|
|
|
302
|
-
|
|
303
|
-
|
|
266
|
+
return sum(
|
|
267
|
+
await Promise.all(
|
|
268
|
+
codeNames.map(async (codeName) => {
|
|
269
|
+
const filePath = `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`;
|
|
270
|
+
if (await exists(filePath)) {
|
|
271
|
+
await unlink(filePath);
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
return 0;
|
|
275
|
+
}),
|
|
276
|
+
),
|
|
304
277
|
);
|
|
278
|
+
}
|
|
305
279
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
280
|
+
private genDateTag(index: number, baseDate: Date = new Date()): string {
|
|
281
|
+
const date = new Date(baseDate.getTime() + index * 1000);
|
|
282
|
+
const pad = (num: number, size: number = 2) => num.toString().padStart(size, "0");
|
|
283
|
+
return (
|
|
284
|
+
date.getFullYear().toString() +
|
|
285
|
+
pad(date.getMonth() + 1) +
|
|
286
|
+
pad(date.getDate()) +
|
|
287
|
+
pad(date.getHours()) +
|
|
288
|
+
pad(date.getMinutes()) +
|
|
289
|
+
pad(date.getSeconds())
|
|
315
290
|
);
|
|
316
|
-
return _.sum(res);
|
|
317
291
|
}
|
|
318
292
|
|
|
319
293
|
/**
|
|
@@ -325,6 +299,7 @@ export class Migrator {
|
|
|
325
299
|
*/
|
|
326
300
|
async generatePreparedCodes(): Promise<number> {
|
|
327
301
|
const { preparedCodes } = await this.getStatus();
|
|
302
|
+
Naite.t("migrator:generatePreparedCodes:preparedCodes", preparedCodes);
|
|
328
303
|
if (preparedCodes.length === 0) {
|
|
329
304
|
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
330
305
|
return 0;
|
|
@@ -335,322 +310,54 @@ export class Migrator {
|
|
|
335
310
|
|
|
336
311
|
for (const [index, pcode] of preparedCodes.entries()) {
|
|
337
312
|
if (pcode.formatted) {
|
|
338
|
-
const dateTag =
|
|
339
|
-
.plus({ seconds: index })
|
|
340
|
-
.toFormat("yyyyMMddHHmmss");
|
|
313
|
+
const dateTag = this.genDateTag(index);
|
|
341
314
|
const filePath = `${migrationsDir}/${dateTag}_${pcode.title}.ts`;
|
|
342
|
-
await writeFile(filePath, pcode.formatted
|
|
343
|
-
console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
|
|
315
|
+
await writeFile(filePath, pcode.formatted);
|
|
316
|
+
!isTest() && console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
|
|
344
317
|
}
|
|
345
318
|
}
|
|
346
319
|
|
|
347
320
|
return preparedCodes.length;
|
|
348
321
|
}
|
|
349
322
|
|
|
350
|
-
|
|
351
|
-
* pending 마이그레이션 목록을 삭제합니다.
|
|
352
|
-
*
|
|
353
|
-
* CLI에서 사용됩니다.
|
|
354
|
-
*/
|
|
355
|
-
async clearPendingList(): Promise<void> {
|
|
356
|
-
const [, pendingList] = (await this.targets.pending.migrate.list()) as [
|
|
357
|
-
unknown,
|
|
358
|
-
{
|
|
359
|
-
file: string;
|
|
360
|
-
directory: string;
|
|
361
|
-
}[],
|
|
362
|
-
];
|
|
363
|
-
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
364
|
-
const delList = pendingList.map((df) => {
|
|
365
|
-
return path.join(migrationsDir, df.file);
|
|
366
|
-
});
|
|
367
|
-
for (let p of delList) {
|
|
368
|
-
if (await exists(p)) {
|
|
369
|
-
await unlink(p);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* 마이그레이션 코드 파일을 확인합니다.
|
|
376
|
-
*
|
|
377
|
-
* CLI에서 사용됩니다.
|
|
378
|
-
*/
|
|
379
|
-
async check(): Promise<void> {
|
|
380
|
-
const codes = await this.compareMigrations(this.targets.compare!);
|
|
381
|
-
if (codes.length === 0) {
|
|
382
|
-
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// 현재 생성된 코드 표기
|
|
387
|
-
console.table(codes, ["type", "title"]);
|
|
388
|
-
console.log(codes[0]);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* 마이그레이션을 수행합니다.
|
|
393
|
-
*
|
|
394
|
-
* runAction이 인자로 들어온 타겟들에 대해 주어진 동작(apply/rollback)을 수행한다면,
|
|
395
|
-
* 이 함수는 생성자로 들어온 connection(knex)들에 대해 마이그레이션을 수행합니다.
|
|
396
|
-
*
|
|
397
|
-
* CLI에서 사용됩니다.
|
|
398
|
-
*/
|
|
399
|
-
async run(): Promise<void> {
|
|
400
|
-
// pending 마이그레이션 확인
|
|
401
|
-
const [, pendingList] = await this.targets.pending.migrate.list();
|
|
402
|
-
if (pendingList.length > 0) {
|
|
403
|
-
console.log(
|
|
404
|
-
chalk.red("pending 된 마이그레이션이 존재합니다."),
|
|
405
|
-
pendingList.map((pending: any) => pending.file)
|
|
406
|
-
);
|
|
407
|
-
|
|
408
|
-
// pending이 있는 경우 Shadow DB 테스트 진행 여부 컨펌
|
|
409
|
-
const answer = await prompts({
|
|
410
|
-
type: "confirm",
|
|
411
|
-
name: "value",
|
|
412
|
-
message: "Shadow DB 테스트를 진행하시겠습니까?",
|
|
413
|
-
initial: true,
|
|
414
|
-
});
|
|
415
|
-
if (answer.value === false) {
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
console.time(chalk.blue("Migrator - runShadowTest"));
|
|
420
|
-
await this.runShadowTest();
|
|
421
|
-
console.timeEnd(chalk.blue("Migrator - runShadowTest"));
|
|
422
|
-
await Promise.all(
|
|
423
|
-
this.targets.apply.map(async (applyDb) => {
|
|
424
|
-
const label = chalk.green(
|
|
425
|
-
`APPLIED ${
|
|
426
|
-
applyDb.client.connectionSettings.host
|
|
427
|
-
} ${applyDb.client.database()}`
|
|
428
|
-
);
|
|
429
|
-
console.time(label);
|
|
430
|
-
const [,] = await applyDb.migrate.latest();
|
|
431
|
-
console.timeEnd(label);
|
|
432
|
-
})
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Entity-DB간 비교하여 코드 생성 리턴
|
|
437
|
-
const codes = await this.compareMigrations(this.targets.compare!);
|
|
438
|
-
if (codes.length === 0) {
|
|
439
|
-
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// 현재 생성된 코드 표기
|
|
444
|
-
console.table(codes, ["type", "title"]);
|
|
445
|
-
|
|
446
|
-
/* DEBUG: 디버깅용 코드
|
|
447
|
-
codes.map((code) => console.log(code.formatted));
|
|
448
|
-
process.exit();
|
|
449
|
-
*/
|
|
450
|
-
|
|
451
|
-
// 실제 파일 생성 프롬프트
|
|
452
|
-
const answer = await prompts({
|
|
453
|
-
type: "confirm",
|
|
454
|
-
name: "value",
|
|
455
|
-
message: "마이그레이션 코드를 생성하시겠습니까?",
|
|
456
|
-
initial: false,
|
|
457
|
-
});
|
|
458
|
-
if (answer.value === false) {
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// 실제 코드 생성
|
|
463
|
-
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
464
|
-
|
|
465
|
-
for (const [index, code] of codes.entries()) {
|
|
466
|
-
if (code.formatted) {
|
|
467
|
-
const dateTag = DateTime.local()
|
|
468
|
-
.plus({ seconds: index })
|
|
469
|
-
.toFormat("yyyyMMddHHmmss");
|
|
470
|
-
const filePath = `${migrationsDir}/${dateTag}_${code.title}.ts`;
|
|
471
|
-
await writeFile(filePath, code.formatted!);
|
|
472
|
-
console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* 타겟으로 지정된 DB를 롤백합니다.
|
|
479
|
-
*
|
|
480
|
-
* runAction이 인자로 들어온 타겟들에 대해 주어진 동작(apply/rollback)을 수행한다면,
|
|
481
|
-
* 이 함수는 생성자로 들어온 connection(knex)들에 대해 롤백을 수행합니다.
|
|
482
|
-
*
|
|
483
|
-
* CLI에서 사용됩니다.
|
|
484
|
-
*/
|
|
485
|
-
async rollback() {
|
|
486
|
-
console.time(chalk.red("rollback:"));
|
|
487
|
-
const rollbackAllResult = await Promise.all(
|
|
488
|
-
this.targets.apply.map(async (db) => {
|
|
489
|
-
await db.migrate.forceFreeMigrationsLock();
|
|
490
|
-
return db.migrate.rollback(undefined, false);
|
|
491
|
-
})
|
|
492
|
-
);
|
|
493
|
-
console.dir({ rollbackAllResult }, { depth: null });
|
|
494
|
-
console.timeEnd(chalk.red("rollback:"));
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Shadow DB 테스트를 진행합니다.
|
|
498
|
-
*
|
|
499
|
-
* Sonamu UI에서 사용됩니다.
|
|
500
|
-
*
|
|
501
|
-
* @returns Shadow DB 테스트 결과
|
|
502
|
-
*/
|
|
503
|
-
async runShadowTest(): Promise<
|
|
504
|
-
{
|
|
505
|
-
connKey: string;
|
|
506
|
-
batchNo: number;
|
|
507
|
-
applied: string[];
|
|
508
|
-
}[]
|
|
509
|
-
> {
|
|
510
|
-
// ShadowDB 생성 후 테스트 진행
|
|
511
|
-
const tdb = knex(Sonamu.dbConfig.test);
|
|
512
|
-
const tdbConn = Sonamu.dbConfig.test
|
|
513
|
-
.connection as Knex.MySql2ConnectionConfig;
|
|
514
|
-
const shadowDatabase = tdbConn.database + "__migration_shadow";
|
|
515
|
-
const tmpSqlPath = `/tmp/${shadowDatabase}.sql`;
|
|
516
|
-
|
|
517
|
-
// 테스트DB 덤프 후 Database명 치환
|
|
518
|
-
console.log(
|
|
519
|
-
chalk.magenta(`${tdbConn.database}의 데이터 ${tmpSqlPath}로 덤프`)
|
|
520
|
-
);
|
|
521
|
-
execSync(
|
|
522
|
-
`mysqldump -h${tdbConn.host} -P${tdbConn.port ?? 3306} -u${tdbConn.user} -p'${tdbConn.password}' ${tdbConn.database} --single-transaction --no-create-db --triggers > ${tmpSqlPath};`
|
|
523
|
-
);
|
|
524
|
-
execSync(
|
|
525
|
-
`sed -i'' -e 's/\`${tdbConn.database}\`/\`${shadowDatabase}\`/g' ${tmpSqlPath};`
|
|
526
|
-
);
|
|
527
|
-
|
|
528
|
-
// 기존 ShadowDB 리셋
|
|
529
|
-
console.log(chalk.magenta(`${shadowDatabase} 리셋`));
|
|
530
|
-
await tdb.raw(`DROP DATABASE IF EXISTS \`${shadowDatabase}\`;`);
|
|
531
|
-
await tdb.raw(`CREATE DATABASE \`${shadowDatabase}\`;`);
|
|
532
|
-
|
|
533
|
-
// ShadowDB 테이블 + 데이터 생성
|
|
534
|
-
console.log(chalk.magenta(`${shadowDatabase} 데이터베이스 생성`));
|
|
535
|
-
execSync(
|
|
536
|
-
`mysql -h${tdbConn.host} -P${tdbConn.port ?? 3306} -u${tdbConn.user} -p'${tdbConn.password}' ${shadowDatabase} < ${tmpSqlPath};`
|
|
537
|
-
);
|
|
538
|
-
|
|
539
|
-
// shadow db 테스트 진행
|
|
540
|
-
const sdb = knex({
|
|
541
|
-
...Sonamu.dbConfig.test,
|
|
542
|
-
connection: {
|
|
543
|
-
...tdbConn,
|
|
544
|
-
database: shadowDatabase,
|
|
545
|
-
password: tdbConn.password,
|
|
546
|
-
},
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
// shadow db 테스트 진행
|
|
550
|
-
try {
|
|
551
|
-
const [batchNo, applied] = await sdb.migrate.latest();
|
|
552
|
-
console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
|
|
553
|
-
batchNo,
|
|
554
|
-
applied,
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
// 생성한 Shadow DB 삭제
|
|
558
|
-
console.log(chalk.magenta(`${shadowDatabase} 삭제`));
|
|
559
|
-
await tdb.raw(`DROP DATABASE IF EXISTS \`${shadowDatabase}\`;`);
|
|
560
|
-
|
|
561
|
-
return [
|
|
562
|
-
{
|
|
563
|
-
connKey: "shadow",
|
|
564
|
-
batchNo,
|
|
565
|
-
applied,
|
|
566
|
-
},
|
|
567
|
-
];
|
|
568
|
-
} catch (e) {
|
|
569
|
-
console.error(e);
|
|
570
|
-
throw new ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");
|
|
571
|
-
} finally {
|
|
572
|
-
await tdb.destroy();
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* 모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제합니다.
|
|
578
|
-
*
|
|
579
|
-
* CLI에서 사용됩니다.
|
|
580
|
-
*
|
|
581
|
-
* @returns
|
|
582
|
-
*/
|
|
583
|
-
async resetAll() {
|
|
584
|
-
const answer = await prompts({
|
|
585
|
-
type: "confirm",
|
|
586
|
-
name: "value",
|
|
587
|
-
message: "모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제하시겠습니까?",
|
|
588
|
-
initial: false,
|
|
589
|
-
});
|
|
590
|
-
if (answer.value === false) {
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
console.time(chalk.red("rollback-all:"));
|
|
595
|
-
const rollbackAllResult = await Promise.all(
|
|
596
|
-
this.targets.apply.map(async (db) => {
|
|
597
|
-
await db.migrate.forceFreeMigrationsLock();
|
|
598
|
-
return db.migrate.rollback(undefined, true);
|
|
599
|
-
})
|
|
600
|
-
);
|
|
601
|
-
console.log({ rollbackAllResult });
|
|
602
|
-
console.timeEnd(chalk.red("rollback-all:"));
|
|
603
|
-
|
|
604
|
-
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
605
|
-
console.time(chalk.red("delete migration files"));
|
|
606
|
-
execSync(`rm -f ${migrationsDir}/*`);
|
|
607
|
-
execSync(`rm -f ${migrationsDir.replace("/src/", "/dist/")}/*`);
|
|
608
|
-
console.timeEnd(chalk.red("delete migration files"));
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
private async compareMigrations(
|
|
612
|
-
compareDB: Knex
|
|
613
|
-
): Promise<GenMigrationCode[]> {
|
|
323
|
+
async compareMigrations(compareDB: Knex): Promise<GenMigrationCode[]> {
|
|
614
324
|
// Entity 순회하여 싱크
|
|
615
325
|
const entityIds = EntityManager.getAllIds();
|
|
616
326
|
|
|
617
327
|
// 조인테이블 포함하여 Entity에서 MigrationSet 추출
|
|
618
328
|
const entitySetsWithJoinTable = entityIds
|
|
619
329
|
.filter((entityId) => EntityManager.get(entityId).props.length > 0)
|
|
620
|
-
.map((entityId) =>
|
|
621
|
-
getMigrationSetFromEntity(EntityManager.get(entityId))
|
|
622
|
-
);
|
|
330
|
+
.map((entityId) => getMigrationSetFromEntity(EntityManager.get(entityId)));
|
|
623
331
|
|
|
624
332
|
// 조인테이블만 추출
|
|
625
|
-
const joinTablesWithDup = entitySetsWithJoinTable
|
|
626
|
-
.map((entitySet) => entitySet.joinTables)
|
|
627
|
-
.flat();
|
|
333
|
+
const joinTablesWithDup = entitySetsWithJoinTable.flatMap((entitySet) => entitySet.joinTables);
|
|
628
334
|
// 중복 제거 (중복인 경우 indexes를 병합)
|
|
629
|
-
const joinTables = Object.values(
|
|
630
|
-
|
|
631
|
-
).map((tables) => {
|
|
335
|
+
const joinTables = Object.values(group(joinTablesWithDup, (jt) => jt.table)).map((tables) => {
|
|
336
|
+
assert(tables !== undefined, "tables is undefined");
|
|
632
337
|
if (tables.length === 1) {
|
|
633
338
|
return tables[0];
|
|
634
339
|
}
|
|
635
340
|
return {
|
|
636
341
|
...tables[0],
|
|
637
|
-
indexes:
|
|
342
|
+
indexes: unique(
|
|
638
343
|
tables.flatMap((t) => t.indexes),
|
|
639
|
-
(index) => [index.type, ...index.columns.sort()].join("-")
|
|
344
|
+
(index) => [index.type, ...index.columns.sort()].join("-"),
|
|
640
345
|
),
|
|
641
346
|
};
|
|
642
347
|
});
|
|
643
348
|
|
|
644
349
|
// 조인테이블 포함하여 MigrationSet 배열
|
|
645
|
-
const entitySets: MigrationSet[] = [
|
|
646
|
-
...entitySetsWithJoinTable,
|
|
647
|
-
...joinTables,
|
|
648
|
-
];
|
|
350
|
+
const entitySets: MigrationSet[] = [...entitySetsWithJoinTable, ...joinTables];
|
|
649
351
|
|
|
650
352
|
const codes: GenMigrationCode[] = (
|
|
651
353
|
await Promise.all(
|
|
652
354
|
entitySets.map(async (entitySet) => {
|
|
653
|
-
const dbSet = await getMigrationSetFromDB(
|
|
355
|
+
const dbSet = await PostgreSQLSchemaReader.getMigrationSetFromDB(
|
|
356
|
+
compareDB,
|
|
357
|
+
entitySet.table,
|
|
358
|
+
);
|
|
359
|
+
Naite.t(`migrator:compareMigrations:entitySet:${entitySet.table}`, entitySet);
|
|
360
|
+
Naite.t(`migrator:compareMigrations:dbSet:${entitySet.table}`, dbSet);
|
|
654
361
|
|
|
655
362
|
if (dbSet === null) {
|
|
656
363
|
// 기존 테이블 없음, 새로 테이블 생성
|
|
@@ -659,13 +366,13 @@ export class Migrator {
|
|
|
659
366
|
// 기존 테이블 존재하는 케이스
|
|
660
367
|
return await generateAlterCode(entitySet, dbSet);
|
|
661
368
|
}
|
|
662
|
-
})
|
|
369
|
+
}),
|
|
663
370
|
)
|
|
664
371
|
).flat();
|
|
665
372
|
|
|
666
373
|
// normal 타입이 앞으로, foreign이 뒤로
|
|
667
374
|
codes.sort((codeA, codeB) => {
|
|
668
|
-
if (codeA.type === "foreign" && codeB.type
|
|
375
|
+
if (codeA.type === "foreign" && codeB.type === "normal") {
|
|
669
376
|
return 1;
|
|
670
377
|
} else if (codeA.type === "normal" && codeB.type === "foreign") {
|
|
671
378
|
return -1;
|
|
@@ -678,17 +385,67 @@ export class Migrator {
|
|
|
678
385
|
}
|
|
679
386
|
|
|
680
387
|
/**
|
|
681
|
-
*
|
|
388
|
+
* Shadow DB 테스트를 진행합니다.
|
|
682
389
|
*
|
|
683
|
-
*
|
|
390
|
+
* Sonamu UI에서 사용됩니다.
|
|
684
391
|
*
|
|
685
|
-
* @returns
|
|
392
|
+
* @returns Shadow DB 테스트 결과
|
|
686
393
|
*/
|
|
687
|
-
async
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
)
|
|
394
|
+
async runShadowTest(): Promise<MigrationResult> {
|
|
395
|
+
const tdbConn = Sonamu.dbConfig.test.connection as Knex.PgConnectionConfig;
|
|
396
|
+
const shadowDatabase = `${tdbConn.database}__migration_shadow`;
|
|
397
|
+
|
|
398
|
+
// 테스트 상황에서는 트랜잭션을 초기화하고, 새 데이터베이스 커넥션을 가져와야 함
|
|
399
|
+
if (isTest()) {
|
|
400
|
+
await DB.clearTestTransaction();
|
|
401
|
+
await DB.destroy();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 기존 Shadow DB 삭제 후 Shadow DB 생성
|
|
405
|
+
const tdb = knex(Sonamu.dbConfig.test);
|
|
406
|
+
!isTest() && console.log(chalk.magenta(`${shadowDatabase} 삭제`));
|
|
407
|
+
await tdb.raw(`DROP DATABASE IF EXISTS ${shadowDatabase}`);
|
|
408
|
+
await tdb.raw(`CREATE DATABASE ${shadowDatabase} TEMPLATE ${tdbConn.database}`);
|
|
409
|
+
|
|
410
|
+
// Shadow DB에 연결
|
|
411
|
+
const sdb = knex({
|
|
412
|
+
...Sonamu.dbConfig.test,
|
|
413
|
+
connection: {
|
|
414
|
+
...tdbConn,
|
|
415
|
+
database: shadowDatabase,
|
|
416
|
+
password: tdbConn.password,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// shadow DB 테스트 진행
|
|
421
|
+
try {
|
|
422
|
+
const [batchNo, applied] = await sdb.migrate.latest();
|
|
423
|
+
!isTest() &&
|
|
424
|
+
console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
|
|
425
|
+
batchNo,
|
|
426
|
+
applied,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
return [
|
|
430
|
+
{
|
|
431
|
+
connKey: "shadow",
|
|
432
|
+
batchNo,
|
|
433
|
+
applied,
|
|
434
|
+
},
|
|
435
|
+
];
|
|
436
|
+
} catch (e) {
|
|
437
|
+
console.error(e);
|
|
438
|
+
throw new ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");
|
|
439
|
+
} finally {
|
|
440
|
+
// Shadow DB 연결 종료
|
|
441
|
+
await sdb.destroy();
|
|
442
|
+
|
|
443
|
+
// Shadow DB 삭제
|
|
444
|
+
!isTest() && console.log(chalk.magenta(`${shadowDatabase} 삭제`));
|
|
445
|
+
await tdb.raw(`DROP DATABASE IF EXISTS ${shadowDatabase}`);
|
|
446
|
+
|
|
447
|
+
// Test DB 연결 종료
|
|
448
|
+
await tdb.destroy();
|
|
449
|
+
}
|
|
693
450
|
}
|
|
694
451
|
}
|