sonamu 0.5.7 → 0.7.0
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 +13 -2
- package/dist/api/caster.d.ts.map +1 -1
- package/dist/api/caster.js +71 -2
- 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 +258 -2
- package/dist/api/config.d.ts +90 -0
- package/dist/api/config.d.ts.map +1 -0
- package/dist/api/config.js +25 -0
- package/dist/api/context.d.ts +4 -2
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +3 -2
- package/dist/api/decorators.d.ts +20 -6
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +235 -2
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +9 -2
- package/dist/api/sonamu.d.ts +10 -24
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +514 -2
- 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 +6 -1
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +15 -2
- package/dist/bin/cli.js +519 -2
- package/dist/bin/hot-hook-register.d.ts +11 -0
- package/dist/bin/hot-hook-register.d.ts.map +1 -0
- package/dist/bin/hot-hook-register.js +21 -0
- 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 +95 -2
- 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 +390 -2
- 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 +54 -2
- package/dist/database/db.d.ts +6 -21
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +129 -2
- 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 +109 -2
- package/dist/database/puri.d.ts +41 -23
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +601 -2
- package/dist/database/puri.types.d.ts +25 -6
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +6 -2
- 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 +14 -2
- 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 +365 -2
- package/dist/entity/entity-manager.d.ts +167 -2
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +130 -2
- package/dist/entity/entity.d.ts +5 -3
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +750 -2
- 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 +29 -2
- 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 +85 -2
- 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 +79 -2
- package/dist/file-storage/file-storage.js +75 -2
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -2
- 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 +614 -2
- 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 +213 -2
- package/dist/migration/migrator.d.ts +24 -82
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +330 -2
- 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 +3 -2
- 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 +95 -0
- package/dist/naite/naite.d.ts.map +1 -0
- package/dist/naite/naite.js +316 -0
- package/dist/stream/index.js +3 -2
- package/dist/stream/sse.d.ts +2 -2
- package/dist/stream/sse.d.ts.map +1 -1
- package/dist/stream/sse.js +38 -2
- package/dist/syncer/api-parser.d.ts +10 -0
- package/dist/syncer/api-parser.d.ts.map +1 -0
- package/dist/syncer/api-parser.js +240 -0
- package/dist/syncer/checksum.d.ts +21 -0
- package/dist/syncer/checksum.d.ts.map +1 -0
- package/dist/syncer/checksum.js +98 -0
- package/dist/syncer/code-generator.d.ts +20 -0
- package/dist/syncer/code-generator.d.ts.map +1 -0
- package/dist/syncer/code-generator.js +161 -0
- package/dist/syncer/entity-operations.d.ts +17 -0
- package/dist/syncer/entity-operations.d.ts.map +1 -0
- package/dist/syncer/entity-operations.js +59 -0
- package/dist/syncer/file-patterns.d.ts +29 -0
- package/dist/syncer/file-patterns.d.ts.map +1 -0
- package/dist/syncer/file-patterns.js +38 -0
- package/dist/syncer/index.d.ts +6 -0
- package/dist/syncer/index.d.ts.map +1 -1
- package/dist/syncer/index.js +9 -2
- package/dist/syncer/module-loader.d.ts +35 -0
- package/dist/syncer/module-loader.d.ts.map +1 -0
- package/dist/syncer/module-loader.js +87 -0
- package/dist/syncer/syncer.d.ts +98 -106
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +422 -2
- package/dist/template/entity-converter.d.ts +14 -0
- package/dist/template/entity-converter.d.ts.map +1 -0
- package/dist/template/entity-converter.js +108 -0
- package/dist/template/helpers.d.ts +23 -0
- package/dist/template/helpers.d.ts.map +1 -0
- package/dist/template/helpers.js +64 -0
- package/dist/{templates → template/implementations}/entity.template.d.ts +3 -3
- package/dist/template/implementations/entity.template.d.ts.map +1 -0
- package/dist/template/implementations/entity.template.js +86 -0
- package/dist/{templates → template/implementations}/generated.template.d.ts +3 -4
- package/dist/template/implementations/generated.template.d.ts.map +1 -0
- package/dist/template/implementations/generated.template.js +249 -0
- package/dist/{templates → template/implementations}/generated_http.template.d.ts +3 -4
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -0
- package/dist/template/implementations/generated_http.template.js +131 -0
- package/dist/{templates → template/implementations}/generated_sso.template.d.ts +4 -5
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -0
- package/dist/template/implementations/generated_sso.template.js +134 -0
- package/dist/{templates → template/implementations}/init_types.template.d.ts +3 -3
- package/dist/template/implementations/init_types.template.d.ts.map +1 -0
- package/dist/template/implementations/init_types.template.js +38 -0
- package/dist/template/implementations/model.template.d.ts +17 -0
- package/dist/template/implementations/model.template.d.ts.map +1 -0
- package/dist/template/implementations/model.template.js +181 -0
- package/dist/{templates → template/implementations}/model_test.template.d.ts +3 -3
- package/dist/template/implementations/model_test.template.d.ts.map +1 -0
- package/dist/template/implementations/model_test.template.js +35 -0
- package/dist/{templates → template/implementations}/service.template.d.ts +6 -6
- package/dist/template/implementations/service.template.d.ts.map +1 -0
- package/dist/template/implementations/service.template.js +201 -0
- package/dist/{templates → template/implementations}/view_enums_buttonset.template.d.ts +3 -3
- package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_buttonset.template.js +31 -0
- package/dist/{templates → template/implementations}/view_enums_dropdown.template.d.ts +3 -4
- package/dist/template/implementations/view_enums_dropdown.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_dropdown.template.js +50 -0
- package/dist/{templates → template/implementations}/view_enums_select.template.d.ts +3 -3
- package/dist/template/implementations/view_enums_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_select.template.js +55 -0
- package/dist/{templates → template/implementations}/view_form.template.d.ts +5 -5
- package/dist/template/implementations/view_form.template.d.ts.map +1 -0
- package/dist/template/implementations/view_form.template.js +337 -0
- package/dist/{templates → template/implementations}/view_id_all_select.template.d.ts +3 -3
- package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_id_all_select.template.js +31 -0
- package/dist/{templates → template/implementations}/view_id_async_select.template.d.ts +3 -3
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_id_async_select.template.js +105 -0
- package/dist/{templates → template/implementations}/view_list.template.d.ts +5 -13
- package/dist/template/implementations/view_list.template.d.ts.map +1 -0
- package/dist/template/implementations/view_list.template.js +475 -0
- package/dist/template/implementations/view_list_columns.template.d.ts +17 -0
- package/dist/template/implementations/view_list_columns.template.d.ts.map +1 -0
- package/dist/template/implementations/view_list_columns.template.js +49 -0
- package/dist/{templates → template/implementations}/view_search_input.template.d.ts +3 -3
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -0
- package/dist/template/implementations/view_search_input.template.js +64 -0
- package/dist/template/index.d.ts +7 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +8 -0
- 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 +49 -0
- package/dist/template/template.d.ts.map +1 -0
- package/dist/template/template.js +60 -0
- package/dist/template/zod-converter.d.ts +51 -0
- package/dist/template/zod-converter.d.ts.map +1 -0
- package/dist/template/zod-converter.js +449 -0
- 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 +89 -2
- 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 +623 -2
- package/dist/types/types.d.ts +747 -143
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +546 -2
- package/dist/typings/knex.d.js +3 -2
- package/dist/utils/async-utils.d.ts +7 -0
- package/dist/utils/async-utils.d.ts.map +1 -1
- package/dist/utils/async-utils.js +57 -2
- package/dist/utils/console-util.d.ts +2 -0
- package/dist/utils/console-util.d.ts.map +1 -0
- package/dist/utils/console-util.js +6 -0
- package/dist/utils/controller.d.ts +1 -0
- package/dist/utils/controller.d.ts.map +1 -1
- package/dist/utils/controller.js +29 -2
- package/dist/utils/esm-utils.d.ts +39 -0
- package/dist/utils/esm-utils.d.ts.map +1 -0
- package/dist/utils/esm-utils.js +49 -0
- 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 +17 -2
- package/dist/utils/lodash-able.d.ts.map +1 -1
- package/dist/utils/lodash-able.js +6 -2
- package/dist/utils/model.js +22 -2
- 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 +89 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +60 -0
- package/dist/utils/process-utils.d.ts +13 -0
- package/dist/utils/process-utils.d.ts.map +1 -0
- package/dist/utils/process-utils.js +36 -0
- 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 +46 -2
- 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 +10 -7
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +72 -2
- 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 +19 -2
- package/package.json +65 -27
- 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 +178 -535
- package/src/api/config.ts +125 -0
- package/src/api/context.ts +7 -17
- package/src/api/decorators.ts +176 -46
- package/src/api/index.ts +2 -2
- package/src/api/sonamu.ts +190 -167
- package/src/api/validator.ts +83 -0
- package/src/bin/build-config.ts +8 -1
- package/src/bin/cli.ts +258 -124
- package/src/bin/hot-hook-register.ts +22 -0
- 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 +40 -96
- 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 +229 -148
- package/src/database/puri.types.ts +76 -30
- package/src/database/transaction-context.ts +1 -1
- package/src/database/upsert-builder.ts +262 -132
- package/src/entity/entity-manager.ts +48 -36
- package/src/entity/entity.ts +330 -248
- 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 -10
- package/src/migration/code-generation.ts +185 -172
- package/src/migration/migration-set.ts +80 -293
- package/src/migration/migrator.ts +199 -571
- 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 +415 -0
- package/src/shared/web.shared.ts.txt +20 -24
- package/src/stream/sse.ts +5 -5
- package/src/syncer/api-parser.ts +282 -0
- package/src/syncer/checksum.ts +140 -0
- package/src/syncer/code-generator.ts +198 -0
- package/src/syncer/entity-operations.ts +65 -0
- package/src/syncer/file-patterns.ts +56 -0
- package/src/syncer/index.ts +6 -0
- package/src/syncer/module-loader.ts +128 -0
- package/src/syncer/syncer.ts +389 -1453
- package/src/template/entity-converter.ts +114 -0
- package/src/template/helpers.ts +81 -0
- package/src/{templates → template/implementations}/entity.template.ts +7 -7
- package/src/{templates → template/implementations}/generated.template.ts +101 -101
- package/src/{templates → template/implementations}/generated_http.template.ts +27 -57
- package/src/template/implementations/generated_sso.template.ts +151 -0
- package/src/{templates → template/implementations}/init_types.template.ts +5 -7
- package/src/{templates → template/implementations}/model.template.ts +52 -43
- package/src/{templates → template/implementations}/model_test.template.ts +5 -5
- package/src/{templates → template/implementations}/service.template.ts +66 -82
- package/src/{templates → template/implementations}/view_enums_buttonset.template.ts +3 -3
- package/src/{templates → template/implementations}/view_enums_dropdown.template.ts +4 -20
- package/src/{templates → template/implementations}/view_enums_select.template.ts +4 -4
- package/src/{templates → template/implementations}/view_form.template.ts +40 -83
- package/src/{templates → template/implementations}/view_id_all_select.template.ts +3 -3
- package/src/{templates → template/implementations}/view_id_async_select.template.ts +10 -24
- package/src/{templates → template/implementations}/view_list.template.ts +60 -152
- package/src/{templates → template/implementations}/view_list_columns.template.ts +5 -11
- package/src/{templates → template/implementations}/view_search_input.template.ts +3 -3
- package/src/template/index.ts +6 -0
- package/src/template/template-manager.ts +166 -0
- package/src/template/template-types.ts +16 -0
- package/src/template/template.ts +105 -0
- package/src/template/zod-converter.ts +525 -0
- package/src/testing/_relation-graph.ts +18 -11
- package/src/testing/fixture-manager.ts +472 -359
- package/src/types/types.ts +553 -308
- package/src/typings/knex.d.ts +7 -9
- package/src/utils/async-utils.ts +23 -10
- package/src/utils/console-util.ts +4 -0
- package/src/utils/controller.ts +3 -0
- package/src/utils/esm-utils.ts +59 -0
- 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 +99 -0
- package/src/utils/process-utils.ts +46 -0
- package/src/utils/sql-parser.ts +23 -5
- package/src/utils/type-utils.ts +83 -0
- package/src/utils/utils.ts +66 -43
- package/src/utils/zod-error.ts +3 -4
- package/dist/api/base-frame.js.map +0 -1
- package/dist/api/caster.js.map +0 -1
- package/dist/api/code-converters.js.map +0 -1
- package/dist/api/context.js.map +0 -1
- package/dist/api/decorators.js.map +0 -1
- package/dist/api/index.js.map +0 -1
- package/dist/api/sonamu.js.map +0 -1
- package/dist/bin/build-config.js.map +0 -1
- 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 -3
- package/dist/bin/cli-wrapper.js.map +0 -1
- package/dist/bin/cli.js.map +0 -1
- package/dist/database/_batch_update.js.map +0 -1
- package/dist/database/base-model.js.map +0 -1
- package/dist/database/code-generator.js.map +0 -1
- package/dist/database/db.js.map +0 -1
- 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 -2
- package/dist/database/knex-plugins/knex-on-duplicate-update.js.map +0 -1
- package/dist/database/puri-wrapper.js.map +0 -1
- package/dist/database/puri.js.map +0 -1
- package/dist/database/puri.types.js.map +0 -1
- package/dist/database/transaction-context.js.map +0 -1
- package/dist/database/upsert-builder.js.map +0 -1
- package/dist/entity/entity-manager.js.map +0 -1
- 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 -2
- package/dist/entity/entity-utils.js.map +0 -1
- package/dist/entity/entity.js.map +0 -1
- package/dist/exceptions/error-handler.js.map +0 -1
- package/dist/exceptions/so-exceptions.js.map +0 -1
- package/dist/file-storage/driver.js.map +0 -1
- package/dist/file-storage/file-storage.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/migration/code-generation.js.map +0 -1
- package/dist/migration/migration-set.js.map +0 -1
- package/dist/migration/migrator.js.map +0 -1
- package/dist/migration/types.js.map +0 -1
- package/dist/stream/index.js.map +0 -1
- package/dist/stream/sse.js.map +0 -1
- package/dist/syncer/index.js.map +0 -1
- package/dist/syncer/syncer.js.map +0 -1
- package/dist/templates/base-template.d.ts +0 -13
- package/dist/templates/base-template.d.ts.map +0 -1
- package/dist/templates/base-template.js +0 -2
- package/dist/templates/base-template.js.map +0 -1
- package/dist/templates/entity.template.d.ts.map +0 -1
- package/dist/templates/entity.template.js +0 -2
- package/dist/templates/entity.template.js.map +0 -1
- package/dist/templates/generated.template.d.ts.map +0 -1
- package/dist/templates/generated.template.js +0 -2
- package/dist/templates/generated.template.js.map +0 -1
- package/dist/templates/generated_http.template.d.ts.map +0 -1
- package/dist/templates/generated_http.template.js +0 -2
- package/dist/templates/generated_http.template.js.map +0 -1
- package/dist/templates/generated_sso.template.d.ts.map +0 -1
- package/dist/templates/generated_sso.template.js +0 -2
- package/dist/templates/generated_sso.template.js.map +0 -1
- package/dist/templates/index.d.ts +0 -2
- package/dist/templates/index.d.ts.map +0 -1
- package/dist/templates/index.js +0 -2
- package/dist/templates/index.js.map +0 -1
- package/dist/templates/init_types.template.d.ts.map +0 -1
- package/dist/templates/init_types.template.js +0 -2
- package/dist/templates/init_types.template.js.map +0 -1
- package/dist/templates/model.template.d.ts +0 -17
- package/dist/templates/model.template.d.ts.map +0 -1
- package/dist/templates/model.template.js +0 -2
- package/dist/templates/model.template.js.map +0 -1
- package/dist/templates/model_test.template.d.ts.map +0 -1
- package/dist/templates/model_test.template.js +0 -2
- package/dist/templates/model_test.template.js.map +0 -1
- package/dist/templates/service.template.d.ts.map +0 -1
- package/dist/templates/service.template.js +0 -2
- package/dist/templates/service.template.js.map +0 -1
- package/dist/templates/view_enums_buttonset.template.d.ts.map +0 -1
- package/dist/templates/view_enums_buttonset.template.js +0 -2
- package/dist/templates/view_enums_buttonset.template.js.map +0 -1
- package/dist/templates/view_enums_dropdown.template.d.ts.map +0 -1
- package/dist/templates/view_enums_dropdown.template.js +0 -2
- package/dist/templates/view_enums_dropdown.template.js.map +0 -1
- package/dist/templates/view_enums_select.template.d.ts.map +0 -1
- package/dist/templates/view_enums_select.template.js +0 -2
- package/dist/templates/view_enums_select.template.js.map +0 -1
- package/dist/templates/view_form.template.d.ts.map +0 -1
- package/dist/templates/view_form.template.js +0 -2
- package/dist/templates/view_form.template.js.map +0 -1
- package/dist/templates/view_id_all_select.template.d.ts.map +0 -1
- package/dist/templates/view_id_all_select.template.js +0 -2
- package/dist/templates/view_id_all_select.template.js.map +0 -1
- package/dist/templates/view_id_async_select.template.d.ts.map +0 -1
- package/dist/templates/view_id_async_select.template.js +0 -2
- package/dist/templates/view_id_async_select.template.js.map +0 -1
- package/dist/templates/view_list.template.d.ts.map +0 -1
- package/dist/templates/view_list.template.js +0 -2
- package/dist/templates/view_list.template.js.map +0 -1
- package/dist/templates/view_list_columns.template.d.ts +0 -17
- package/dist/templates/view_list_columns.template.d.ts.map +0 -1
- package/dist/templates/view_list_columns.template.js +0 -2
- package/dist/templates/view_list_columns.template.js.map +0 -1
- package/dist/templates/view_search_input.template.d.ts.map +0 -1
- package/dist/templates/view_search_input.template.js +0 -2
- package/dist/templates/view_search_input.template.js.map +0 -1
- package/dist/testing/_relation-graph.js.map +0 -1
- package/dist/testing/fixture-manager.js.map +0 -1
- package/dist/types/types.js.map +0 -1
- package/dist/typings/knex.d.js.map +0 -1
- package/dist/utils/async-utils.js.map +0 -1
- package/dist/utils/controller.js.map +0 -1
- package/dist/utils/fs-utils.js.map +0 -1
- package/dist/utils/lodash-able.js.map +0 -1
- package/dist/utils/model.js.map +0 -1
- package/dist/utils/sql-parser.js.map +0 -1
- package/dist/utils/utils.js.map +0 -1
- package/dist/utils/zod-error.js.map +0 -1
- package/src/bin/cli-wrapper.ts +0 -75
- package/src/database/knex-plugins/knex-on-duplicate-update.ts +0 -45
- package/src/entity/entity-utils.ts +0 -291
- package/src/templates/base-template.ts +0 -19
- package/src/templates/generated_sso.template.ts +0 -138
- package/src/templates/index.ts +0 -1
|
@@ -1,27 +1,38 @@
|
|
|
1
|
+
import assert from "assert";
|
|
1
2
|
import chalk from "chalk";
|
|
2
|
-
import
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import inflection from "inflection";
|
|
6
|
+
import knex, { type Knex } from "knex";
|
|
7
|
+
import { unique } from "radashi";
|
|
8
|
+
import { inspect } from "util";
|
|
3
9
|
import { Sonamu } from "../api";
|
|
10
|
+
import { BaseModel } from "../database/base-model";
|
|
11
|
+
import type { SonamuDBConfig } from "../database/db";
|
|
12
|
+
import { type UBRef, UpsertBuilder } from "../database/upsert-builder";
|
|
13
|
+
import type { Entity } from "../entity/entity";
|
|
4
14
|
import { EntityManager } from "../entity/entity-manager";
|
|
5
15
|
import {
|
|
6
|
-
EntityProp,
|
|
7
|
-
FixtureImportResult,
|
|
8
|
-
FixtureRecord,
|
|
9
|
-
FixtureSearchOptions,
|
|
10
|
-
ManyToManyRelationProp,
|
|
16
|
+
type EntityProp,
|
|
17
|
+
type FixtureImportResult,
|
|
18
|
+
type FixtureRecord,
|
|
19
|
+
type FixtureSearchOptions,
|
|
11
20
|
isBelongsToOneRelationProp,
|
|
12
21
|
isHasManyRelationProp,
|
|
13
22
|
isManyToManyRelationProp,
|
|
14
23
|
isOneToOneRelationProp,
|
|
15
24
|
isRelationProp,
|
|
16
25
|
isVirtualProp,
|
|
26
|
+
type ManyToManyRelationProp,
|
|
17
27
|
} from "../types/types";
|
|
18
|
-
import { Entity } from "../entity/entity";
|
|
19
|
-
import inflection from "inflection";
|
|
20
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
21
28
|
import { RelationGraph } from "./_relation-graph";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
|
|
30
|
+
/** 사용자 지정 중복 확인 컬럼 (entityId별로 지정) */
|
|
31
|
+
export interface DuplicateCheckOptions {
|
|
32
|
+
columns?: {
|
|
33
|
+
[entityId: string]: string[];
|
|
34
|
+
};
|
|
35
|
+
}
|
|
25
36
|
|
|
26
37
|
export class FixtureManagerClass {
|
|
27
38
|
private _tdb: Knex | null = null;
|
|
@@ -49,6 +60,12 @@ export class FixtureManagerClass {
|
|
|
49
60
|
|
|
50
61
|
private relationGraph = new RelationGraph();
|
|
51
62
|
|
|
63
|
+
// UpsertBuilder 기반 import를 위한 상태
|
|
64
|
+
private builder: UpsertBuilder = new UpsertBuilder();
|
|
65
|
+
private fixtureRefMap: Map<string, UBRef> = new Map();
|
|
66
|
+
private uuidToFixtureId: Map<string, string> = new Map();
|
|
67
|
+
private skippedFixtures: Map<string, { entityId: string; existingId: number }> = new Map();
|
|
68
|
+
|
|
52
69
|
init() {
|
|
53
70
|
if (this._tdb !== null) {
|
|
54
71
|
return;
|
|
@@ -57,89 +74,19 @@ export class FixtureManagerClass {
|
|
|
57
74
|
const tConn = Sonamu.dbConfig.test.connection as Knex.ConnectionConfig & {
|
|
58
75
|
port?: number;
|
|
59
76
|
};
|
|
60
|
-
const pConn = Sonamu.dbConfig.production_master
|
|
61
|
-
|
|
77
|
+
const pConn = Sonamu.dbConfig.production_master.connection as Knex.ConnectionConfig & {
|
|
78
|
+
port?: number;
|
|
79
|
+
};
|
|
62
80
|
if (
|
|
63
|
-
`${tConn.host ?? "localhost"}:${tConn.port ??
|
|
64
|
-
|
|
65
|
-
}` ===
|
|
66
|
-
`${pConn.host ?? "localhost"}:${pConn.port ?? 3306}/${pConn.database}`
|
|
81
|
+
`${tConn.host ?? "localhost"}:${tConn.port ?? 5432}/${tConn.database}` ===
|
|
82
|
+
`${pConn.host ?? "localhost"}:${pConn.port ?? 5432}/${pConn.database}`
|
|
67
83
|
) {
|
|
68
|
-
throw new Error(
|
|
69
|
-
`테스트DB와 프로덕션DB에 동일한 데이터베이스가 사용되었습니다.`
|
|
70
|
-
);
|
|
84
|
+
throw new Error(`테스트DB와 프로덕션DB에 동일한 데이터베이스가 사용되었습니다.`);
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
this.tdb = knex(Sonamu.dbConfig.test);
|
|
75
|
-
this.fdb = knex(Sonamu.dbConfig.
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async cleanAndSeed(usingTables?: string[]) {
|
|
79
|
-
const tableNames: string[] = await (async () => {
|
|
80
|
-
if (usingTables) {
|
|
81
|
-
return usingTables;
|
|
82
|
-
}
|
|
83
|
-
if (this.cachedTableNames) {
|
|
84
|
-
return this.cachedTableNames;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const [tables] = await this.tdb.raw(
|
|
88
|
-
`SHOW TABLE STATUS WHERE Engine IS NOT NULL AND Name != 'migrations'`
|
|
89
|
-
);
|
|
90
|
-
const tableNames = tables.map(
|
|
91
|
-
(tableInfo: { Name: string }) => tableInfo["Name"]
|
|
92
|
-
);
|
|
93
|
-
this.cachedTableNames = tableNames;
|
|
94
|
-
return tableNames;
|
|
95
|
-
})();
|
|
96
|
-
|
|
97
|
-
// migrations 제외한 테이블 목록
|
|
98
|
-
const tableListStr = tableNames.join(", ");
|
|
99
|
-
|
|
100
|
-
// 한 번에 모든 테이블 체크섬 확인
|
|
101
|
-
const [fdbChecksumRows] = await this.fdb.raw<
|
|
102
|
-
[{ Table: string; Checksum: number }[]]
|
|
103
|
-
>(`CHECKSUM TABLE ${tableListStr}`);
|
|
104
|
-
const [tdbChecksumRows] = await this.tdb.raw<
|
|
105
|
-
[{ Table: string; Checksum: number }[]]
|
|
106
|
-
>(`CHECKSUM TABLE ${tableListStr}`);
|
|
107
|
-
|
|
108
|
-
// 체크섬 맵 생성
|
|
109
|
-
const fdbChecksums = new Map(
|
|
110
|
-
fdbChecksumRows.map((row) => [row.Table.split(".").pop()!, row.Checksum])
|
|
111
|
-
);
|
|
112
|
-
const tdbChecksums = new Map(
|
|
113
|
-
tdbChecksumRows.map((row) => [row.Table.split(".").pop()!, row.Checksum])
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
// 변경된 테이블들만 처리
|
|
117
|
-
const changedTables = tableNames.filter(
|
|
118
|
-
(tableName) => fdbChecksums.get(tableName) !== tdbChecksums.get(tableName)
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// 병렬로 truncate + insert 실행
|
|
122
|
-
await this.tdb.transaction(async (trx) => {
|
|
123
|
-
await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);
|
|
124
|
-
|
|
125
|
-
await Promise.all(
|
|
126
|
-
changedTables.map(async (tableName) => {
|
|
127
|
-
await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);
|
|
128
|
-
await trx(tableName).truncate();
|
|
129
|
-
const rawQuery = `INSERT INTO ${
|
|
130
|
-
(Sonamu.dbConfig.test.connection as Knex.ConnectionConfig).database
|
|
131
|
-
}.${tableName}
|
|
132
|
-
SELECT * FROM ${
|
|
133
|
-
(Sonamu.dbConfig.fixture_local.connection as Knex.ConnectionConfig)
|
|
134
|
-
.database
|
|
135
|
-
}.${tableName}`;
|
|
136
|
-
await trx.raw(rawQuery);
|
|
137
|
-
})
|
|
138
|
-
);
|
|
139
|
-
await trx.raw(`SET FOREIGN_KEY_CHECKS = 1`);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// console.timeEnd("FIXTURE-CleanAndSeed");
|
|
89
|
+
this.fdb = knex(Sonamu.dbConfig.fixture_remote);
|
|
143
90
|
}
|
|
144
91
|
|
|
145
92
|
async getChecksum(db: Knex, tableName: string) {
|
|
@@ -147,68 +94,48 @@ export class FixtureManagerClass {
|
|
|
147
94
|
return checksumRow.Checksum;
|
|
148
95
|
}
|
|
149
96
|
|
|
97
|
+
/**
|
|
98
|
+
이제 FixtureManager.sync() 는 checksum 비교 없이 create database template 으로 수행합니다.
|
|
99
|
+
*/
|
|
150
100
|
async sync() {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
101
|
+
const fixtureConn = Sonamu.dbConfig.fixture_remote.connection as Knex.PgConnectionConfig;
|
|
102
|
+
const testConn = Sonamu.dbConfig.test.connection as Knex.PgConnectionConfig;
|
|
103
|
+
|
|
104
|
+
// PostgreSQL 패스워드 환경변수 설정
|
|
105
|
+
const pgEnv = { PGPASSWORD: testConn.password || "" };
|
|
106
|
+
|
|
107
|
+
// 1. 연결 강제 종료
|
|
108
|
+
execSync(
|
|
109
|
+
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "
|
|
110
|
+
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
111
|
+
FROM pg_stat_activity
|
|
112
|
+
WHERE datname = '${testConn.database}'
|
|
113
|
+
AND pid <> pg_backend_pid();
|
|
114
|
+
"`,
|
|
115
|
+
{ stdio: "inherit", env: { ...process.env, ...pgEnv } as NodeJS.ProcessEnv },
|
|
158
116
|
);
|
|
159
117
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (remoteChecksum !== localChecksum) {
|
|
171
|
-
await this.fdb.transaction(async (transaction) => {
|
|
172
|
-
await transaction.raw(`SET FOREIGN_KEY_CHECKS = 0`);
|
|
173
|
-
await transaction(tableName).truncate();
|
|
174
|
-
|
|
175
|
-
const rows = await frdb(tableName);
|
|
176
|
-
if (rows.length === 0) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
118
|
+
execSync(
|
|
119
|
+
`psql -h ${fixtureConn.host} -p ${fixtureConn.port ?? 5432} -U ${fixtureConn.user} -d postgres -c "
|
|
120
|
+
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
121
|
+
FROM pg_stat_activity
|
|
122
|
+
WHERE datname = '${fixtureConn.database}'
|
|
123
|
+
AND pid <> pg_backend_pid();
|
|
124
|
+
"`,
|
|
125
|
+
{ stdio: "inherit", env: { ...process.env, ...pgEnv } as NodeJS.ProcessEnv },
|
|
126
|
+
);
|
|
179
127
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return Object.fromEntries(
|
|
185
|
-
Object.entries(row).map(([key, value]) => {
|
|
186
|
-
if (value === null) {
|
|
187
|
-
return [key, null];
|
|
188
|
-
} else if (typeof value === "boolean") {
|
|
189
|
-
return [key, value ? 1 : 0];
|
|
190
|
-
} else if (
|
|
191
|
-
typeof value === "object" &&
|
|
192
|
-
!(value instanceof Date)
|
|
193
|
-
) {
|
|
194
|
-
return [key, JSON.stringify(value)];
|
|
195
|
-
} else {
|
|
196
|
-
return [key, value];
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
);
|
|
200
|
-
})
|
|
201
|
-
)
|
|
202
|
-
.into(tableName);
|
|
203
|
-
console.log("OK");
|
|
204
|
-
await transaction.raw(`SET FOREIGN_KEY_CHECKS = 1`);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
})
|
|
128
|
+
// 2. DROP DATABASE (별도 실행!)
|
|
129
|
+
execSync(
|
|
130
|
+
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "DROP DATABASE IF EXISTS \\"${testConn.database}\\""`,
|
|
131
|
+
{ stdio: "inherit", env: { ...process.env, ...pgEnv } as NodeJS.ProcessEnv },
|
|
208
132
|
);
|
|
209
|
-
console.log(chalk.magenta("DONE!"));
|
|
210
133
|
|
|
211
|
-
|
|
134
|
+
// 3. CREATE DATABASE
|
|
135
|
+
execSync(
|
|
136
|
+
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "CREATE DATABASE \\"${testConn.database}\\" TEMPLATE \\"${fixtureConn.database}\\""`,
|
|
137
|
+
{ stdio: "inherit", env: { ...process.env, ...pgEnv } as NodeJS.ProcessEnv },
|
|
138
|
+
);
|
|
212
139
|
}
|
|
213
140
|
|
|
214
141
|
private visitedRecords = new Set<string>();
|
|
@@ -216,18 +143,18 @@ export class FixtureManagerClass {
|
|
|
216
143
|
// 방문 기록 초기화 (새로운 import 작업 시작)
|
|
217
144
|
this.visitedRecords.clear();
|
|
218
145
|
|
|
219
|
-
const queries =
|
|
146
|
+
const queries = unique(
|
|
220
147
|
(
|
|
221
148
|
await Promise.all(
|
|
222
149
|
ids.map(async (id) => {
|
|
223
150
|
return await this.getImportQueries(entityId, "id", id);
|
|
224
|
-
})
|
|
151
|
+
}),
|
|
225
152
|
)
|
|
226
|
-
).flat()
|
|
153
|
+
).flat(),
|
|
227
154
|
);
|
|
228
155
|
|
|
229
156
|
const wdb = BaseModel.getDB("w");
|
|
230
|
-
for (
|
|
157
|
+
for (const query of queries) {
|
|
231
158
|
const [rsh] = await wdb.raw(query);
|
|
232
159
|
console.log({
|
|
233
160
|
query,
|
|
@@ -236,11 +163,7 @@ export class FixtureManagerClass {
|
|
|
236
163
|
}
|
|
237
164
|
}
|
|
238
165
|
|
|
239
|
-
async getImportQueries(
|
|
240
|
-
entityId: string,
|
|
241
|
-
field: string,
|
|
242
|
-
id: number
|
|
243
|
-
): Promise<string[]> {
|
|
166
|
+
async getImportQueries(entityId: string, field: string, id: number): Promise<string[]> {
|
|
244
167
|
const recordKey = `${entityId}#${field}#${id}`;
|
|
245
168
|
|
|
246
169
|
// 순환 참조 방지: 이미 방문한 레코드는 스킵
|
|
@@ -260,9 +183,9 @@ export class FixtureManagerClass {
|
|
|
260
183
|
}
|
|
261
184
|
|
|
262
185
|
// 픽스쳐DB, 실DB
|
|
263
|
-
const fixtureDatabase = (Sonamu.dbConfig.fixture_remote.connection as
|
|
186
|
+
const fixtureDatabase = (Sonamu.dbConfig.fixture_remote.connection as Knex.ConnectionConfig)
|
|
264
187
|
.database;
|
|
265
|
-
const realDatabase = (Sonamu.dbConfig.production_master.connection as
|
|
188
|
+
const realDatabase = (Sonamu.dbConfig.production_master.connection as Knex.ConnectionConfig)
|
|
266
189
|
.database;
|
|
267
190
|
|
|
268
191
|
const selfQuery = `INSERT IGNORE INTO \`${fixtureDatabase}\`.\`${entity.table}\` (SELECT * FROM \`${realDatabase}\`.\`${entity.table}\` WHERE \`id\` = ${id})`;
|
|
@@ -271,8 +194,7 @@ export class FixtureManagerClass {
|
|
|
271
194
|
.filter(
|
|
272
195
|
([, relation]) =>
|
|
273
196
|
isBelongsToOneRelationProp(relation) ||
|
|
274
|
-
(isOneToOneRelationProp(relation) &&
|
|
275
|
-
relation.customJoinClause === undefined)
|
|
197
|
+
(isOneToOneRelationProp(relation) && relation.customJoinClause === undefined),
|
|
276
198
|
)
|
|
277
199
|
.map(([, relation]) => {
|
|
278
200
|
/*
|
|
@@ -288,15 +210,13 @@ export class FixtureManagerClass {
|
|
|
288
210
|
if (isOneToOneRelationProp(relation) && !relation.hasJoinColumn) {
|
|
289
211
|
const relatedEntity = EntityManager.get(relation.with);
|
|
290
212
|
const relatedIdColumnName = relatedEntity.props.find(
|
|
291
|
-
(p) => isRelationProp(p) && p.with === entity.id
|
|
213
|
+
(p) => isRelationProp(p) && p.with === entity.id,
|
|
292
214
|
)?.name;
|
|
293
215
|
if (!relatedIdColumnName) {
|
|
294
|
-
throw new Error(
|
|
295
|
-
`${relatedEntity.id}의 ${entity.id} 관계 프롭을 찾을 수 없습니다.`
|
|
296
|
-
);
|
|
216
|
+
throw new Error(`${relatedEntity.id}의 ${entity.id} 관계 프롭을 찾을 수 없습니다.`);
|
|
297
217
|
}
|
|
298
218
|
field = `${relatedIdColumnName}_id`;
|
|
299
|
-
id = row
|
|
219
|
+
id = row.id;
|
|
300
220
|
} else {
|
|
301
221
|
field = "id";
|
|
302
222
|
id = row[`${relation.name}_id`];
|
|
@@ -312,10 +232,10 @@ export class FixtureManagerClass {
|
|
|
312
232
|
const relQueries = await Promise.all(
|
|
313
233
|
args.map(async (args) => {
|
|
314
234
|
return this.getImportQueries(args.entityId, args.field, args.id);
|
|
315
|
-
})
|
|
235
|
+
}),
|
|
316
236
|
);
|
|
317
237
|
|
|
318
|
-
return [...
|
|
238
|
+
return [...unique(relQueries.reverse().flat()), selfQuery];
|
|
319
239
|
}
|
|
320
240
|
|
|
321
241
|
async destroy() {
|
|
@@ -333,17 +253,17 @@ export class FixtureManagerClass {
|
|
|
333
253
|
async getFixtures(
|
|
334
254
|
sourceDBName: keyof SonamuDBConfig,
|
|
335
255
|
targetDBName: keyof SonamuDBConfig,
|
|
336
|
-
searchOptions: FixtureSearchOptions
|
|
256
|
+
searchOptions: FixtureSearchOptions,
|
|
257
|
+
duplicateCheck?: DuplicateCheckOptions,
|
|
337
258
|
) {
|
|
338
259
|
const sourceDB = knex(Sonamu.dbConfig[sourceDBName]);
|
|
339
260
|
const targetDB = knex(Sonamu.dbConfig[targetDBName]);
|
|
261
|
+
|
|
340
262
|
const { entityId, field, value, searchType } = searchOptions;
|
|
341
263
|
|
|
342
264
|
const entity = EntityManager.get(entityId);
|
|
343
265
|
const column =
|
|
344
|
-
entity.props.find((prop) => prop.name === field)?.type === "relation"
|
|
345
|
-
? `${field}_id`
|
|
346
|
-
: field;
|
|
266
|
+
entity.props.find((prop) => prop.name === field)?.type === "relation" ? `${field}_id` : field;
|
|
347
267
|
|
|
348
268
|
let query = sourceDB(entity.table);
|
|
349
269
|
if (searchType === "equals") {
|
|
@@ -360,11 +280,11 @@ export class FixtureManagerClass {
|
|
|
360
280
|
const fixtures: FixtureRecord[] = [];
|
|
361
281
|
for (const row of rows) {
|
|
362
282
|
const initialRecordsLength = fixtures.length;
|
|
363
|
-
const newRecords = await this.createFixtureRecord(entity, row
|
|
283
|
+
const newRecords = await this.createFixtureRecord(entity, row, {
|
|
284
|
+
_db: sourceDB,
|
|
285
|
+
});
|
|
364
286
|
fixtures.push(...newRecords);
|
|
365
|
-
const currentFixtureRecord = fixtures.find(
|
|
366
|
-
(r) => r.fixtureId === `${entityId}#${row.id}`
|
|
367
|
-
);
|
|
287
|
+
const currentFixtureRecord = fixtures.find((r) => r.fixtureId === `${entityId}#${row.id}`);
|
|
368
288
|
|
|
369
289
|
if (currentFixtureRecord) {
|
|
370
290
|
// 현재 fixture로부터 생성된 fetchedRecords 설정
|
|
@@ -378,23 +298,26 @@ export class FixtureManagerClass {
|
|
|
378
298
|
for await (const fixture of fixtures) {
|
|
379
299
|
const entity = EntityManager.get(fixture.entityId);
|
|
380
300
|
|
|
381
|
-
//
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
301
|
+
// 사용자 지정 컬럼 기준 중복 확인 → target
|
|
302
|
+
const customColumns = duplicateCheck?.columns?.[fixture.entityId];
|
|
303
|
+
if (customColumns && customColumns.length > 0) {
|
|
304
|
+
const customDuplicateRow = await this.checkDuplicateByColumns(
|
|
305
|
+
targetDB,
|
|
306
|
+
entity,
|
|
307
|
+
fixture,
|
|
308
|
+
customColumns,
|
|
309
|
+
);
|
|
310
|
+
if (customDuplicateRow) {
|
|
311
|
+
const [record] = await this.createFixtureRecord(entity, customDuplicateRow, {
|
|
312
|
+
singleRecord: true,
|
|
313
|
+
_db: targetDB,
|
|
314
|
+
});
|
|
315
|
+
fixture.target = record;
|
|
316
|
+
}
|
|
390
317
|
}
|
|
391
318
|
|
|
392
|
-
//
|
|
393
|
-
const uniqueRow = await this.checkUniqueViolation(
|
|
394
|
-
targetDB,
|
|
395
|
-
entity,
|
|
396
|
-
fixture
|
|
397
|
-
);
|
|
319
|
+
// Unique index 기준 중복 확인 → fixture.unique
|
|
320
|
+
const uniqueRow = await this.checkUniqueViolation(targetDB, entity, fixture);
|
|
398
321
|
if (uniqueRow) {
|
|
399
322
|
const [record] = await this.createFixtureRecord(entity, uniqueRow, {
|
|
400
323
|
singleRecord: true,
|
|
@@ -404,21 +327,33 @@ export class FixtureManagerClass {
|
|
|
404
327
|
}
|
|
405
328
|
}
|
|
406
329
|
|
|
407
|
-
|
|
330
|
+
await targetDB.destroy();
|
|
331
|
+
await sourceDB.destroy();
|
|
332
|
+
|
|
333
|
+
return unique(fixtures, (f) => f.fixtureId);
|
|
408
334
|
}
|
|
409
335
|
|
|
410
336
|
async createFixtureRecord(
|
|
411
337
|
entity: Entity,
|
|
412
|
-
row:
|
|
338
|
+
row: {
|
|
339
|
+
id: number;
|
|
340
|
+
[key: string]: string | number | boolean | null;
|
|
341
|
+
},
|
|
413
342
|
options?: {
|
|
414
343
|
singleRecord?: boolean;
|
|
415
344
|
_db?: Knex;
|
|
416
|
-
}
|
|
345
|
+
},
|
|
417
346
|
): Promise<FixtureRecord[]> {
|
|
418
347
|
const records: FixtureRecord[] = [];
|
|
419
348
|
const visitedEntities = new Set<string>();
|
|
420
349
|
|
|
421
|
-
const create = async (
|
|
350
|
+
const create = async (
|
|
351
|
+
entity: Entity,
|
|
352
|
+
row: {
|
|
353
|
+
id: number;
|
|
354
|
+
[key: string]: string | number | boolean | null;
|
|
355
|
+
},
|
|
356
|
+
) => {
|
|
422
357
|
const fixtureId = `${entity.id}#${row.id}`;
|
|
423
358
|
if (visitedEntities.has(fixtureId)) {
|
|
424
359
|
return;
|
|
@@ -451,9 +386,7 @@ export class FixtureManagerClass {
|
|
|
451
386
|
const fromColumn = `${inflection.singularize(entity.table)}_id`;
|
|
452
387
|
const toColumn = `${inflection.singularize(relatedEntity.table)}_id`;
|
|
453
388
|
|
|
454
|
-
const relatedIds = await db(throughTable)
|
|
455
|
-
.where(fromColumn, row.id)
|
|
456
|
-
.pluck(toColumn);
|
|
389
|
+
const relatedIds = await db(throughTable).where(fromColumn, row.id).pluck(toColumn);
|
|
457
390
|
record.columns[prop.name].value = relatedIds;
|
|
458
391
|
} else if (isHasManyRelationProp(prop)) {
|
|
459
392
|
const relatedEntity = EntityManager.get(prop.with);
|
|
@@ -464,12 +397,10 @@ export class FixtureManagerClass {
|
|
|
464
397
|
} else if (isOneToOneRelationProp(prop) && !prop.hasJoinColumn) {
|
|
465
398
|
const relatedEntity = EntityManager.get(prop.with);
|
|
466
399
|
const relatedProp = relatedEntity.props.find(
|
|
467
|
-
(p) => isRelationProp(p) && p.with === entity.id
|
|
400
|
+
(p) => isRelationProp(p) && p.with === entity.id,
|
|
468
401
|
);
|
|
469
402
|
if (relatedProp) {
|
|
470
|
-
const relatedRow = await db(relatedEntity.table)
|
|
471
|
-
.where("id", row.id)
|
|
472
|
-
.first();
|
|
403
|
+
const relatedRow = await db(relatedEntity.table).where("id", row.id).first();
|
|
473
404
|
record.columns[prop.name].value = relatedRow?.id;
|
|
474
405
|
}
|
|
475
406
|
} else if (isRelationProp(prop)) {
|
|
@@ -480,9 +411,7 @@ export class FixtureManagerClass {
|
|
|
480
411
|
}
|
|
481
412
|
if (!options?.singleRecord && relatedId) {
|
|
482
413
|
const relatedEntity = EntityManager.get(prop.with);
|
|
483
|
-
const relatedRow = await db(relatedEntity.table)
|
|
484
|
-
.where("id", relatedId)
|
|
485
|
-
.first();
|
|
414
|
+
const relatedRow = await db(relatedEntity.table).where("id", relatedId).first();
|
|
486
415
|
if (relatedRow) {
|
|
487
416
|
await create(relatedEntity, relatedRow);
|
|
488
417
|
}
|
|
@@ -498,217 +427,348 @@ export class FixtureManagerClass {
|
|
|
498
427
|
return records;
|
|
499
428
|
}
|
|
500
429
|
|
|
430
|
+
/**
|
|
431
|
+
* 1. RelationGraph로 fixture 단위 삽입 순서 계산 (self-reference 포함)
|
|
432
|
+
* 2. 순서대로 UpsertBuilder에 등록 (UBRef로 참조 관계 표현)
|
|
433
|
+
* 3. 테이블별 upsert 실행 (ID는 DB가 자동 할당)
|
|
434
|
+
*/
|
|
501
435
|
async insertFixtures(
|
|
502
436
|
dbName: keyof SonamuDBConfig,
|
|
503
|
-
_fixtures: FixtureRecord[]
|
|
504
|
-
) {
|
|
505
|
-
const fixtures =
|
|
437
|
+
_fixtures: FixtureRecord[],
|
|
438
|
+
): Promise<FixtureImportResult[]> {
|
|
439
|
+
const fixtures = unique(_fixtures, (f) => f.fixtureId);
|
|
440
|
+
|
|
441
|
+
// 초기화
|
|
442
|
+
this.builder = new UpsertBuilder();
|
|
443
|
+
this.fixtureRefMap = new Map();
|
|
444
|
+
this.uuidToFixtureId = new Map();
|
|
445
|
+
this.skippedFixtures = new Map();
|
|
506
446
|
|
|
507
|
-
this.relationGraph.buildGraph(fixtures);
|
|
508
|
-
const insertionOrder = this.relationGraph.getInsertionOrder();
|
|
509
447
|
const db = knex(Sonamu.dbConfig[dbName]);
|
|
448
|
+
const results: FixtureImportResult[] = [];
|
|
510
449
|
|
|
511
|
-
|
|
512
|
-
|
|
450
|
+
try {
|
|
451
|
+
// 1. RelationGraph로 fixture 단위 삽입 순서 계산
|
|
452
|
+
this.relationGraph.buildGraph(fixtures);
|
|
453
|
+
const insertionOrder = this.relationGraph.getInsertionOrder();
|
|
513
454
|
|
|
455
|
+
// 2. 순서대로 UpsertBuilder에 등록 (override 체크)
|
|
514
456
|
for (const fixtureId of insertionOrder) {
|
|
515
|
-
const fixture = fixtures.find((f) => f.fixtureId === fixtureId)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
457
|
+
const fixture = fixtures.find((f) => f.fixtureId === fixtureId);
|
|
458
|
+
if (!fixture) continue;
|
|
459
|
+
|
|
460
|
+
const hasTarget = !!fixture.target;
|
|
461
|
+
const hasUnique = !!fixture.unique;
|
|
462
|
+
const hasDuplicate = hasTarget || hasUnique;
|
|
463
|
+
|
|
464
|
+
// 중복이 있고 override=false인 경우: 스킵
|
|
465
|
+
if (hasDuplicate && !fixture.override) {
|
|
466
|
+
// 기존 레코드 ID 저장 (unique 우선, 없으면 target)
|
|
467
|
+
const existingId = fixture.unique?.id ?? fixture.target?.id;
|
|
468
|
+
assert(existingId);
|
|
469
|
+
this.skippedFixtures.set(fixtureId, {
|
|
470
|
+
entityId: fixture.entityId,
|
|
471
|
+
existingId,
|
|
472
|
+
});
|
|
473
|
+
|
|
519
474
|
console.log(
|
|
520
475
|
chalk.yellow(
|
|
521
|
-
`
|
|
522
|
-
)
|
|
476
|
+
`Skipped ${fixture.entityId}#${fixture.id} (existing: #${existingId}, override: false)`,
|
|
477
|
+
),
|
|
523
478
|
);
|
|
524
|
-
|
|
525
|
-
Object.values(f.columns).forEach((column) => {
|
|
526
|
-
if (
|
|
527
|
-
column.prop.type === "relation" &&
|
|
528
|
-
column.prop.with === result.entityId &&
|
|
529
|
-
column.value === fixture.id
|
|
530
|
-
) {
|
|
531
|
-
column.value = result.id;
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
});
|
|
535
|
-
fixture.id = result.id;
|
|
479
|
+
continue;
|
|
536
480
|
}
|
|
537
|
-
}
|
|
538
481
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
482
|
+
this.registerFixture(fixture);
|
|
483
|
+
console.log(
|
|
484
|
+
chalk.blue(
|
|
485
|
+
`Registered ${fixture.entityId}#${fixture.id}${fixture.override ? ` (override existing: #${fixture.target?.id})` : ""}`,
|
|
486
|
+
),
|
|
487
|
+
);
|
|
542
488
|
}
|
|
543
|
-
await trx.raw(`SET FOREIGN_KEY_CHECKS = 1`);
|
|
544
|
-
});
|
|
545
489
|
|
|
546
|
-
|
|
490
|
+
// 3. 테이블별 upsert 실행
|
|
491
|
+
const tableOrder = this.getTableOrder(fixtures);
|
|
492
|
+
|
|
493
|
+
await db.transaction(async (trx) => {
|
|
494
|
+
const insertedIdsByTable = new Map<string, Map<string, number>>();
|
|
495
|
+
|
|
496
|
+
for (const tableName of tableOrder) {
|
|
497
|
+
if (!this.builder.hasTable(tableName)) continue;
|
|
498
|
+
|
|
499
|
+
// upsert 실행 전 uuid 목록 저장
|
|
500
|
+
const table = this.builder.getTable(tableName);
|
|
501
|
+
const uuids = table.rows.map((row) => row.uuid as string);
|
|
502
|
+
|
|
503
|
+
console.log(chalk.blue(`Upserting ${tableName} with ${uuids.length} rows`));
|
|
504
|
+
await this.builder.upsert(trx, tableName);
|
|
505
|
+
|
|
506
|
+
// upsert된 row들의 uuid -> id 매핑 구축
|
|
507
|
+
if (uuids.length > 0) {
|
|
508
|
+
const uuidToId = new Map<string, number>();
|
|
509
|
+
const rows = await trx(tableName).select("uuid", "id").whereIn("uuid", uuids);
|
|
510
|
+
|
|
511
|
+
for (const row of rows) {
|
|
512
|
+
uuidToId.set(row.uuid, row.id);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
insertedIdsByTable.set(tableName, uuidToId);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// 4. ManyToMany 관계 처리
|
|
520
|
+
await this.processManyToManyRelations(trx, fixtures, insertedIdsByTable);
|
|
521
|
+
|
|
522
|
+
// 5. 결과 수집
|
|
523
|
+
for (const fixture of fixtures) {
|
|
524
|
+
const entity = EntityManager.get(fixture.entityId);
|
|
547
525
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
526
|
+
// 스킵된 fixture는 기존 레코드 정보로 결과 추가
|
|
527
|
+
const skipped = this.skippedFixtures.get(fixture.fixtureId);
|
|
528
|
+
if (skipped) {
|
|
529
|
+
results.push({
|
|
530
|
+
entityId: fixture.entityId,
|
|
531
|
+
data: await trx(entity.table).where("id", skipped.existingId).first(),
|
|
532
|
+
});
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const ref = this.fixtureRefMap.get(fixture.fixtureId);
|
|
537
|
+
if (ref) {
|
|
538
|
+
const uuidToId = insertedIdsByTable.get(entity.table);
|
|
539
|
+
const insertedId = uuidToId?.get(ref.uuid);
|
|
540
|
+
|
|
541
|
+
if (insertedId !== undefined) {
|
|
542
|
+
results.push({
|
|
543
|
+
entityId: fixture.entityId,
|
|
544
|
+
data: await trx(entity.table).where("id", insertedId).first(),
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
console.log(
|
|
548
|
+
chalk.green(`Inserted into ${entity.table}: #${fixture.id} -> #${insertedId}`),
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
554
553
|
});
|
|
554
|
+
} finally {
|
|
555
|
+
await db.destroy();
|
|
555
556
|
}
|
|
556
557
|
|
|
557
|
-
return
|
|
558
|
+
return unique(results, (r) => `${r.entityId}#${r.data.id}`);
|
|
558
559
|
}
|
|
559
560
|
|
|
560
|
-
|
|
561
|
-
|
|
561
|
+
/**
|
|
562
|
+
* FixtureRecord를 UpsertBuilder에 등록
|
|
563
|
+
*/
|
|
564
|
+
private registerFixture(fixture: FixtureRecord): UBRef {
|
|
565
|
+
const entity = EntityManager.get(fixture.entityId);
|
|
566
|
+
const row: Record<string, unknown> = {};
|
|
567
|
+
|
|
568
|
+
// Override 모드 판단: target 또는 unique가 있고 override=true인 경우
|
|
569
|
+
const existingRecord = fixture.target ?? fixture.unique;
|
|
570
|
+
const isOverrideMode = fixture.override && existingRecord;
|
|
571
|
+
|
|
562
572
|
for (const [propName, column] of Object.entries(fixture.columns)) {
|
|
563
|
-
|
|
573
|
+
const prop = column.prop;
|
|
574
|
+
|
|
575
|
+
if (isVirtualProp(prop)) {
|
|
564
576
|
continue;
|
|
565
577
|
}
|
|
566
578
|
|
|
567
|
-
|
|
568
|
-
if (
|
|
569
|
-
if (
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
insertData[propName] = new Date(column.value);
|
|
573
|
-
} else {
|
|
574
|
-
insertData[propName] = column.value;
|
|
579
|
+
// id/uuid 처리: Override 모드일 때만 기존 값 사용
|
|
580
|
+
if (propName === "id" || propName === "uuid") {
|
|
581
|
+
if (isOverrideMode && existingRecord) {
|
|
582
|
+
// Override: 기존 레코드의 값 사용 → UPDATE
|
|
583
|
+
row[propName] = existingRecord.columns[propName]?.value;
|
|
575
584
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
585
|
+
// 새 레코드: 제외 → INSERT (DB/UpsertBuilder가 생성)
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (isRelationProp(prop)) {
|
|
590
|
+
if (
|
|
591
|
+
isBelongsToOneRelationProp(prop) ||
|
|
592
|
+
(isOneToOneRelationProp(prop) && prop.hasJoinColumn)
|
|
593
|
+
) {
|
|
594
|
+
const relatedId = column.value as number | null;
|
|
595
|
+
if (relatedId !== null && relatedId !== undefined) {
|
|
596
|
+
const relatedFixtureId = `${prop.with}#${relatedId}`;
|
|
597
|
+
|
|
598
|
+
// 먼저 skip된 fixture인지 확인
|
|
599
|
+
const skippedExistingId = this.skippedFixtures.get(relatedFixtureId)?.existingId;
|
|
600
|
+
if (skippedExistingId !== undefined) {
|
|
601
|
+
// skip된 fixture → target DB의 기존 레코드 id 사용
|
|
602
|
+
row[`${propName}_id`] = skippedExistingId;
|
|
603
|
+
} else {
|
|
604
|
+
const relatedRef = this.fixtureRefMap.get(relatedFixtureId);
|
|
605
|
+
if (relatedRef) {
|
|
606
|
+
// 이미 등록된 fixture 참조 → UBRef 사용
|
|
607
|
+
row[`${propName}_id`] = relatedRef;
|
|
608
|
+
} else {
|
|
609
|
+
// fixtures에 포함되지 않은 레코드 → ID 그대로 사용
|
|
610
|
+
row[`${propName}_id`] = relatedId;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
} else {
|
|
614
|
+
row[`${propName}_id`] = null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// HasMany, ManyToMany는 별도 처리
|
|
618
|
+
} else {
|
|
619
|
+
// 일반 컬럼
|
|
620
|
+
row[propName] = this.convertColumnValue(prop as EntityProp, column.value);
|
|
581
621
|
}
|
|
582
622
|
}
|
|
583
|
-
|
|
623
|
+
|
|
624
|
+
console.log(chalk.blue(`Registering ${entity.table} - ${inspect(row, false, null, true)}`));
|
|
625
|
+
const ref = this.builder.register(entity.table, row);
|
|
626
|
+
this.fixtureRefMap.set(fixture.fixtureId, ref);
|
|
627
|
+
this.uuidToFixtureId.set(ref.uuid, fixture.fixtureId);
|
|
628
|
+
|
|
629
|
+
return ref;
|
|
584
630
|
}
|
|
585
631
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
632
|
+
/**
|
|
633
|
+
* 컬럼 값 변환
|
|
634
|
+
*/
|
|
635
|
+
private convertColumnValue(prop: EntityProp, value: unknown): unknown {
|
|
636
|
+
if (value === null || value === undefined) {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
589
639
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
return
|
|
594
|
-
entityId: fixture.entityId,
|
|
595
|
-
id: uniqueFound.id,
|
|
596
|
-
};
|
|
597
|
-
}
|
|
640
|
+
switch (prop.type) {
|
|
641
|
+
case "json":
|
|
642
|
+
// UpsertBuilder.register에서 JSON.stringify 처리하므로 object 그대로 전달
|
|
643
|
+
return value;
|
|
598
644
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
};
|
|
605
|
-
}
|
|
645
|
+
case "date":
|
|
646
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
647
|
+
return new Date(value);
|
|
648
|
+
}
|
|
649
|
+
return value;
|
|
606
650
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return {
|
|
610
|
-
entityId: fixture.entityId,
|
|
611
|
-
id: fixture.id,
|
|
612
|
-
};
|
|
613
|
-
} catch (err) {
|
|
614
|
-
console.log(err);
|
|
615
|
-
throw err;
|
|
651
|
+
default:
|
|
652
|
+
return value;
|
|
616
653
|
}
|
|
617
654
|
}
|
|
618
655
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
const prop = column.prop as EntityProp;
|
|
626
|
-
if (isManyToManyRelationProp(prop)) {
|
|
627
|
-
const joinTable = (prop as ManyToManyRelationProp).joinTable;
|
|
628
|
-
const relatedIds = column.value as number[];
|
|
629
|
-
|
|
630
|
-
for (const relatedId of relatedIds) {
|
|
631
|
-
if (
|
|
632
|
-
!fixtures.find((f) => f.fixtureId === `${prop.with}#${relatedId}`)
|
|
633
|
-
) {
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
const entity = EntityManager.get(fixture.entityId);
|
|
638
|
-
const relatedEntity = EntityManager.get(prop.with);
|
|
639
|
-
if (!entity || !relatedEntity) {
|
|
640
|
-
throw new Error(
|
|
641
|
-
`Entity not found: ${fixture.entityId}, ${prop.with}`
|
|
642
|
-
);
|
|
643
|
-
}
|
|
656
|
+
/**
|
|
657
|
+
* 테이블 순서 추출 (fixtures에 포함된 테이블만)
|
|
658
|
+
*/
|
|
659
|
+
private getTableOrder(fixtures: FixtureRecord[]): string[] {
|
|
660
|
+
const tables: string[] = [];
|
|
661
|
+
const seen = new Set<string>();
|
|
644
662
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
.limit(1);
|
|
651
|
-
if (found) {
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
const newIds = await db(joinTable).insert({
|
|
656
|
-
[`${inflection.singularize(entity.table)}_id`]: fixture.id,
|
|
657
|
-
[`${inflection.singularize(relatedEntity.table)}_id`]: relatedId,
|
|
658
|
-
});
|
|
659
|
-
console.log(
|
|
660
|
-
chalk.green(
|
|
661
|
-
`Inserted into ${joinTable}: ${entity.table}(${fixture.id}) - ${relatedEntity.table}(${relatedId}) ID: ${newIds}`
|
|
662
|
-
)
|
|
663
|
-
);
|
|
664
|
-
}
|
|
663
|
+
for (const fixture of fixtures) {
|
|
664
|
+
const entity = EntityManager.get(fixture.entityId);
|
|
665
|
+
if (!seen.has(entity.table)) {
|
|
666
|
+
seen.add(entity.table);
|
|
667
|
+
tables.push(entity.table);
|
|
665
668
|
}
|
|
666
669
|
}
|
|
670
|
+
|
|
671
|
+
return tables;
|
|
667
672
|
}
|
|
668
673
|
|
|
669
|
-
async
|
|
670
|
-
|
|
671
|
-
|
|
674
|
+
private async processManyToManyRelations(
|
|
675
|
+
trx: Knex.Transaction,
|
|
676
|
+
fixtures: FixtureRecord[],
|
|
677
|
+
insertedIdsByTable: Map<string, Map<string, number>>,
|
|
678
|
+
): Promise<void> {
|
|
679
|
+
for (const fixture of fixtures) {
|
|
680
|
+
const entity = EntityManager.get(fixture.entityId);
|
|
681
|
+
const sourceRef = this.fixtureRefMap.get(fixture.fixtureId);
|
|
672
682
|
|
|
673
|
-
|
|
674
|
-
const fixtureLoaderEnd = content.indexOf("};", fixtureLoaderStart);
|
|
683
|
+
if (!sourceRef) continue;
|
|
675
684
|
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
content.slice(0, fixtureLoaderEnd) +
|
|
679
|
-
" " +
|
|
680
|
-
code +
|
|
681
|
-
"\n" +
|
|
682
|
-
content.slice(fixtureLoaderEnd);
|
|
685
|
+
const sourceUuidToId = insertedIdsByTable.get(entity.table);
|
|
686
|
+
const sourceId = sourceUuidToId?.get(sourceRef.uuid);
|
|
683
687
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
688
|
+
if (sourceId === undefined) continue;
|
|
689
|
+
|
|
690
|
+
for (const [, column] of Object.entries(fixture.columns)) {
|
|
691
|
+
const prop = column.prop;
|
|
692
|
+
|
|
693
|
+
if (isManyToManyRelationProp(prop) && Array.isArray(column.value)) {
|
|
694
|
+
// 선택되지 않은 ManyToMany 관계는 저장하지 않음
|
|
695
|
+
const targetTable = EntityManager.get(prop.with);
|
|
696
|
+
if (this.builder.hasTable(targetTable.table) === false) continue;
|
|
697
|
+
|
|
698
|
+
const relatedIds = column.value as number[];
|
|
699
|
+
if (relatedIds.length === 0) continue;
|
|
700
|
+
|
|
701
|
+
const joinTable = (prop as ManyToManyRelationProp).joinTable;
|
|
702
|
+
const relatedEntity = EntityManager.get(prop.with);
|
|
703
|
+
|
|
704
|
+
const sourceColumn = `${inflection.singularize(entity.table)}_id`;
|
|
705
|
+
const targetColumn = `${inflection.singularize(relatedEntity.table)}_id`;
|
|
706
|
+
|
|
707
|
+
for (const relatedId of relatedIds) {
|
|
708
|
+
const relatedFixtureId = `${prop.with}#${relatedId}`;
|
|
709
|
+
const relatedRef = this.fixtureRefMap.get(relatedFixtureId);
|
|
710
|
+
|
|
711
|
+
let targetId: number;
|
|
712
|
+
|
|
713
|
+
if (relatedRef) {
|
|
714
|
+
const relatedUuidToId = insertedIdsByTable.get(relatedEntity.table);
|
|
715
|
+
const resolvedId = relatedUuidToId?.get(relatedRef.uuid);
|
|
716
|
+
|
|
717
|
+
if (resolvedId === undefined) {
|
|
718
|
+
console.warn(
|
|
719
|
+
`Related fixture ${relatedFixtureId} not found in insertedIds, skipping`,
|
|
720
|
+
);
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
targetId = resolvedId;
|
|
724
|
+
} else {
|
|
725
|
+
targetId = relatedId;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// JoinTable에 삽입
|
|
729
|
+
const [found] = await trx(joinTable)
|
|
730
|
+
.where({
|
|
731
|
+
[sourceColumn]: sourceId,
|
|
732
|
+
[targetColumn]: targetId,
|
|
733
|
+
})
|
|
734
|
+
.limit(1);
|
|
735
|
+
|
|
736
|
+
if (!found) {
|
|
737
|
+
await trx(joinTable).insert({
|
|
738
|
+
[sourceColumn]: sourceId,
|
|
739
|
+
[targetColumn]: targetId,
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
console.log(
|
|
743
|
+
chalk.green(
|
|
744
|
+
`Inserted into ${joinTable}: ${entity.table}(${sourceId}) - ${relatedEntity.table}(${targetId})`,
|
|
745
|
+
),
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
687
751
|
}
|
|
688
752
|
}
|
|
689
753
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
db: Knex,
|
|
693
|
-
entity: Entity,
|
|
694
|
-
fixture: FixtureRecord
|
|
695
|
-
) {
|
|
696
|
-
const _uniqueIndexes = entity.indexes.filter((i) => i.type === "unique");
|
|
754
|
+
private async checkUniqueViolation(db: Knex, entity: Entity, fixture: FixtureRecord) {
|
|
755
|
+
const _uniqueIndexes = entity.indexes?.filter((i) => i.type === "unique") ?? [];
|
|
697
756
|
|
|
698
|
-
// ManyToMany 관계 테이블의 유니크 제약은 제외
|
|
699
757
|
const uniqueIndexes = _uniqueIndexes.filter((index) =>
|
|
700
|
-
index.columns.every((column) => !column.startsWith(`${entity.table}__`))
|
|
758
|
+
index.columns.every((column) => !column.startsWith(`${entity.table}__`)),
|
|
701
759
|
);
|
|
702
760
|
if (uniqueIndexes.length === 0) {
|
|
703
761
|
return null;
|
|
704
762
|
}
|
|
705
763
|
|
|
706
764
|
let uniqueQuery = db(entity.table);
|
|
765
|
+
let hasCondition = false;
|
|
766
|
+
|
|
707
767
|
for (const index of uniqueIndexes) {
|
|
708
768
|
// 컬럼 중 하나라도 null이면 유니크 제약을 위반하지 않기 때문에 해당 인덱스는 무시
|
|
709
769
|
const containsNull = index.columns.some((column) => {
|
|
710
|
-
const field = column.
|
|
711
|
-
return fixture.columns[field]
|
|
770
|
+
const field = column.replace(/_id$/, "");
|
|
771
|
+
return fixture.columns[field]?.value === null;
|
|
712
772
|
});
|
|
713
773
|
if (containsNull) {
|
|
714
774
|
continue;
|
|
@@ -716,18 +776,71 @@ export class FixtureManagerClass {
|
|
|
716
776
|
|
|
717
777
|
uniqueQuery = uniqueQuery.orWhere((qb) => {
|
|
718
778
|
for (const column of index.columns) {
|
|
719
|
-
const field = column.
|
|
779
|
+
const field = column.replace(/_id$/, "");
|
|
720
780
|
|
|
721
|
-
if (Array.isArray(fixture.columns[field]
|
|
781
|
+
if (Array.isArray(fixture.columns[field]?.value)) {
|
|
722
782
|
qb.whereIn(column, fixture.columns[field].value);
|
|
723
783
|
} else {
|
|
724
|
-
qb.andWhere(column, fixture.columns[field]
|
|
784
|
+
qb.andWhere(column, fixture.columns[field]?.value);
|
|
725
785
|
}
|
|
726
786
|
}
|
|
727
787
|
});
|
|
788
|
+
hasCondition = true;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (!hasCondition) {
|
|
792
|
+
return null;
|
|
728
793
|
}
|
|
794
|
+
|
|
729
795
|
const [uniqueFound] = await uniqueQuery;
|
|
730
796
|
return uniqueFound;
|
|
731
797
|
}
|
|
798
|
+
|
|
799
|
+
private async checkDuplicateByColumns(
|
|
800
|
+
db: Knex,
|
|
801
|
+
entity: Entity,
|
|
802
|
+
fixture: FixtureRecord,
|
|
803
|
+
columns: string[],
|
|
804
|
+
) {
|
|
805
|
+
if (columns.length === 0) {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const whereClause: Record<string, unknown> = {};
|
|
810
|
+
|
|
811
|
+
for (const column of columns) {
|
|
812
|
+
// relation 필드인 경우 _id 붙이기
|
|
813
|
+
const prop = entity.props.find((p) => p.name === column);
|
|
814
|
+
const dbColumn = prop && isRelationProp(prop) ? `${column}_id` : column;
|
|
815
|
+
const value = fixture.columns[column]?.value;
|
|
816
|
+
|
|
817
|
+
// null 값이 포함된 경우 중복 확인 스킵
|
|
818
|
+
if (value === null || value === undefined) {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
whereClause[dbColumn] = value;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const [found] = await db(entity.table).where(whereClause).limit(1);
|
|
826
|
+
return found;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
async addFixtureLoader(code: string) {
|
|
830
|
+
const path = `${Sonamu.apiRootPath}/src/testing/fixture.ts`;
|
|
831
|
+
const content = readFileSync(path).toString();
|
|
832
|
+
|
|
833
|
+
const fixtureLoaderStart = content.indexOf("const fixtureLoader = {");
|
|
834
|
+
const fixtureLoaderEnd = content.indexOf("};", fixtureLoaderStart);
|
|
835
|
+
|
|
836
|
+
if (fixtureLoaderStart !== -1 && fixtureLoaderEnd !== -1) {
|
|
837
|
+
const newContent = `${content.slice(0, fixtureLoaderEnd)} ${code}\n${content.slice(fixtureLoaderEnd)}`;
|
|
838
|
+
|
|
839
|
+
writeFileSync(path, newContent);
|
|
840
|
+
} else {
|
|
841
|
+
throw new Error("Failed to find fixtureLoader in fixture.ts");
|
|
842
|
+
}
|
|
843
|
+
}
|
|
732
844
|
}
|
|
845
|
+
|
|
733
846
|
export const FixtureManager = new FixtureManagerClass();
|