sonamu 0.2.47 → 0.2.49
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/dist/bin/cli-wrapper.js +12 -2
- package/dist/bin/cli-wrapper.js.map +1 -1
- package/dist/bin/cli.js +54 -62
- package/dist/bin/cli.js.map +1 -1
- package/dist/{chunk-5VT5JTY4.js → chunk-HATLA54Z.js} +448 -63
- package/dist/chunk-HATLA54Z.js.map +1 -0
- package/dist/index.d.ts +57 -6
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/src/api/sonamu.ts +20 -2
- package/src/bin/cli-wrapper.ts +20 -1
- package/src/bin/cli.ts +5 -15
- package/src/database/_batch_update.ts +29 -14
- package/src/database/upsert-builder.ts +56 -42
- package/src/testing/fixture-manager.ts +517 -0
- package/src/types/types.ts +31 -0
- package/src/utils/utils.ts +15 -13
- package/dist/chunk-5VT5JTY4.js.map +0 -1
|
@@ -842,19 +842,19 @@ function globAsync(pathPattern) {
|
|
|
842
842
|
});
|
|
843
843
|
}
|
|
844
844
|
async function importMultiple(filePaths, doRefresh = false) {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
845
|
+
const results = [];
|
|
846
|
+
for (const filePath of filePaths) {
|
|
847
|
+
const importPath = "./" + _path2.default.relative(__dirname, filePath);
|
|
848
|
+
if (doRefresh) {
|
|
849
|
+
delete __require.cache[__require.resolve(importPath)];
|
|
850
|
+
}
|
|
851
|
+
const imported = await Promise.resolve().then(() => _interopRequireWildcard(require(importPath)));
|
|
852
|
+
results.push({
|
|
853
|
+
filePath,
|
|
854
|
+
imported
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
return results;
|
|
858
858
|
}
|
|
859
859
|
async function findAppRootPath() {
|
|
860
860
|
const apiRootPath = await findApiRootPath();
|
|
@@ -4215,13 +4215,13 @@ var _uuid = require('uuid');
|
|
|
4215
4215
|
|
|
4216
4216
|
|
|
4217
4217
|
// src/database/_batch_update.ts
|
|
4218
|
-
async function batchUpdate(knex5, tableName,
|
|
4218
|
+
async function batchUpdate(knex5, tableName, ids, rows, chunkSize = 50, trx = null) {
|
|
4219
4219
|
const chunks = [];
|
|
4220
4220
|
for (let i = 0; i < rows.length; i += chunkSize) {
|
|
4221
4221
|
chunks.push(rows.slice(i, i + chunkSize));
|
|
4222
4222
|
}
|
|
4223
4223
|
const executeUpdate = async (chunk, transaction) => {
|
|
4224
|
-
const sql = generateBatchUpdateSQL(knex5, tableName, chunk,
|
|
4224
|
+
const sql = generateBatchUpdateSQL(knex5, tableName, chunk, ids);
|
|
4225
4225
|
return knex5.raw(sql).transacting(transaction);
|
|
4226
4226
|
};
|
|
4227
4227
|
if (trx) {
|
|
@@ -4245,31 +4245,38 @@ function generateKeySetFromData(data) {
|
|
|
4245
4245
|
}
|
|
4246
4246
|
return keySet;
|
|
4247
4247
|
}
|
|
4248
|
-
function generateBatchUpdateSQL(knex5, tableName, data,
|
|
4248
|
+
function generateBatchUpdateSQL(knex5, tableName, data, identifiers) {
|
|
4249
4249
|
const keySet = generateKeySetFromData(data);
|
|
4250
4250
|
const bindings = [];
|
|
4251
|
+
const invalidIdentifiers = identifiers.filter((id) => !keySet.has(id));
|
|
4252
|
+
if (invalidIdentifiers.length > 0) {
|
|
4253
|
+
throw new Error(
|
|
4254
|
+
`Invalid identifiers: ${invalidIdentifiers.join(", ")}. Identifiers must exist in the data`
|
|
4255
|
+
);
|
|
4256
|
+
}
|
|
4251
4257
|
const cases = [];
|
|
4252
4258
|
for (const key of keySet) {
|
|
4253
|
-
if (key
|
|
4259
|
+
if (identifiers.includes(key)) continue;
|
|
4254
4260
|
const rows = [];
|
|
4255
4261
|
for (const row of data) {
|
|
4256
4262
|
if (Object.hasOwnProperty.call(row, key)) {
|
|
4257
|
-
|
|
4258
|
-
|
|
4263
|
+
const whereClause = identifiers.map((id) => `\`${id}\` = ?`).join(" AND ");
|
|
4264
|
+
rows.push(`WHEN (${whereClause}) THEN ?`);
|
|
4265
|
+
bindings.push(...identifiers.map((i) => row[i]), row[key]);
|
|
4259
4266
|
}
|
|
4260
4267
|
}
|
|
4261
4268
|
const whenThen = rows.join(" ");
|
|
4262
4269
|
cases.push(`\`${key}\` = CASE ${whenThen} ELSE \`${key}\` END`);
|
|
4263
4270
|
}
|
|
4264
|
-
const
|
|
4265
|
-
const
|
|
4271
|
+
const whereInClauses = identifiers.map((col) => `${col} IN (${data.map(() => "?").join(", ")})`).join(" AND ");
|
|
4272
|
+
const whereInBindings = identifiers.flatMap(
|
|
4273
|
+
(col) => data.map((row) => row[col])
|
|
4274
|
+
);
|
|
4266
4275
|
const sql = knex5.raw(
|
|
4267
|
-
`UPDATE \`${tableName}\` SET ${cases.join(
|
|
4268
|
-
|
|
4269
|
-
)} WHERE ${identifier} IN (${whereInPlaceholders})`,
|
|
4270
|
-
[...bindings, ...whereInIds]
|
|
4276
|
+
`UPDATE \`${tableName}\` SET ${cases.join(", ")} WHERE ${whereInClauses}`,
|
|
4277
|
+
[...bindings, ...whereInBindings]
|
|
4271
4278
|
);
|
|
4272
|
-
return sql.
|
|
4279
|
+
return sql.toQuery();
|
|
4273
4280
|
}
|
|
4274
4281
|
|
|
4275
4282
|
// src/database/upsert-builder.ts
|
|
@@ -4355,13 +4362,13 @@ var UpsertBuilder = class {
|
|
|
4355
4362
|
uuid: _nullishCoalesce(row.uuid, () => ( uuid))
|
|
4356
4363
|
};
|
|
4357
4364
|
}
|
|
4358
|
-
async upsert(wdb, tableName) {
|
|
4359
|
-
return this.upsertOrInsert(wdb, tableName, "upsert");
|
|
4365
|
+
async upsert(wdb, tableName, chunkSize) {
|
|
4366
|
+
return this.upsertOrInsert(wdb, tableName, "upsert", chunkSize);
|
|
4360
4367
|
}
|
|
4361
|
-
async insertOnly(wdb, tableName) {
|
|
4362
|
-
return this.upsertOrInsert(wdb, tableName, "insert");
|
|
4368
|
+
async insertOnly(wdb, tableName, chunkSize) {
|
|
4369
|
+
return this.upsertOrInsert(wdb, tableName, "insert", chunkSize);
|
|
4363
4370
|
}
|
|
4364
|
-
async upsertOrInsert(wdb, tableName, mode) {
|
|
4371
|
+
async upsertOrInsert(wdb, tableName, mode, chunkSize) {
|
|
4365
4372
|
if (this.hasTable(tableName) === false) {
|
|
4366
4373
|
return [];
|
|
4367
4374
|
}
|
|
@@ -4378,17 +4385,6 @@ var UpsertBuilder = class {
|
|
|
4378
4385
|
)) {
|
|
4379
4386
|
throw new Error(`${tableName} \uD574\uACB0\uB418\uC9C0 \uC54A\uC740 \uCC38\uC870\uAC00 \uC788\uC2B5\uB2C8\uB2E4.`);
|
|
4380
4387
|
}
|
|
4381
|
-
const groups = _lodash2.default.groupBy(
|
|
4382
|
-
table.rows,
|
|
4383
|
-
(row) => Object.entries(row).some(([, value]) => isRefField(value)) ? "selfRef" : "normal"
|
|
4384
|
-
);
|
|
4385
|
-
const targetRows = groups.normal;
|
|
4386
|
-
const q = wdb.insert(targetRows).into(tableName);
|
|
4387
|
-
if (mode === "insert") {
|
|
4388
|
-
await q;
|
|
4389
|
-
} else if (mode === "upsert") {
|
|
4390
|
-
await q.onDuplicateUpdate.apply(q, Object.keys(targetRows[0]));
|
|
4391
|
-
}
|
|
4392
4388
|
const { references, refTables } = Array.from(this.tables).reduce(
|
|
4393
4389
|
(r, [, table2]) => {
|
|
4394
4390
|
const reference = Array.from(table2.references.values()).find(
|
|
@@ -4408,11 +4404,27 @@ var UpsertBuilder = class {
|
|
|
4408
4404
|
const extractFields = _lodash2.default.uniq(references).map(
|
|
4409
4405
|
(reference) => reference.split(".")[1]
|
|
4410
4406
|
);
|
|
4411
|
-
const
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
upsertedRows.map((row) => [row.uuid, row])
|
|
4407
|
+
const groups = _lodash2.default.groupBy(
|
|
4408
|
+
table.rows,
|
|
4409
|
+
(row) => Object.entries(row).some(([, value]) => isRefField(value)) ? "selfRef" : "normal"
|
|
4415
4410
|
);
|
|
4411
|
+
const normalRows = _nullishCoalesce(groups.normal, () => ( []));
|
|
4412
|
+
const selfRefRows = _nullishCoalesce(groups.selfRef, () => ( []));
|
|
4413
|
+
const chunks = chunkSize ? _lodash2.default.chunk(normalRows, chunkSize) : [normalRows];
|
|
4414
|
+
const uuidMap = /* @__PURE__ */ new Map();
|
|
4415
|
+
for (const chunk of chunks) {
|
|
4416
|
+
const q = wdb.insert(chunk).into(tableName);
|
|
4417
|
+
if (mode === "insert") {
|
|
4418
|
+
await q;
|
|
4419
|
+
} else if (mode === "upsert") {
|
|
4420
|
+
await q.onDuplicateUpdate.apply(q, Object.keys(normalRows[0]));
|
|
4421
|
+
}
|
|
4422
|
+
const uuids = chunk.map((row) => row.uuid);
|
|
4423
|
+
const upsertedRows = await wdb(tableName).select(_lodash2.default.uniq(["uuid", "id", ...extractFields])).whereIn("uuid", uuids);
|
|
4424
|
+
upsertedRows.forEach((row) => {
|
|
4425
|
+
uuidMap.set(row.uuid, row);
|
|
4426
|
+
});
|
|
4427
|
+
}
|
|
4416
4428
|
refTables.map((table2) => {
|
|
4417
4429
|
table2.rows = table2.rows.map((row) => {
|
|
4418
4430
|
Object.keys(row).map((key) => {
|
|
@@ -4431,12 +4443,13 @@ var UpsertBuilder = class {
|
|
|
4431
4443
|
return row;
|
|
4432
4444
|
});
|
|
4433
4445
|
});
|
|
4434
|
-
const
|
|
4435
|
-
if (
|
|
4436
|
-
|
|
4437
|
-
|
|
4446
|
+
const allIds = Array.from(uuidMap.values()).map((row) => row.id);
|
|
4447
|
+
if (selfRefRows.length > 0) {
|
|
4448
|
+
table.rows = selfRefRows;
|
|
4449
|
+
const selfRefIds = await this.upsert(wdb, tableName, chunkSize);
|
|
4450
|
+
allIds.push(...selfRefIds);
|
|
4438
4451
|
}
|
|
4439
|
-
return
|
|
4452
|
+
return allIds;
|
|
4440
4453
|
}
|
|
4441
4454
|
async updateBatch(wdb, tableName, options) {
|
|
4442
4455
|
options = _lodash2.default.defaults(options, {
|
|
@@ -4450,17 +4463,12 @@ var UpsertBuilder = class {
|
|
|
4450
4463
|
if (table.rows.length === 0) {
|
|
4451
4464
|
return;
|
|
4452
4465
|
}
|
|
4466
|
+
const whereColumns = Array.isArray(options.where) ? options.where : [_nullishCoalesce(options.where, () => ( "id"))];
|
|
4453
4467
|
const rows = table.rows.map((_row) => {
|
|
4454
4468
|
const { uuid, ...row } = _row;
|
|
4455
4469
|
return row;
|
|
4456
4470
|
});
|
|
4457
|
-
await batchUpdate(
|
|
4458
|
-
wdb,
|
|
4459
|
-
tableName,
|
|
4460
|
-
_nullishCoalesce(options.where, () => ( "id")),
|
|
4461
|
-
rows,
|
|
4462
|
-
options.chunkSize
|
|
4463
|
-
);
|
|
4471
|
+
await batchUpdate(wdb, tableName, whereColumns, rows, options.chunkSize);
|
|
4464
4472
|
}
|
|
4465
4473
|
};
|
|
4466
4474
|
|
|
@@ -4796,6 +4804,7 @@ var SonamuClass = class {
|
|
|
4796
4804
|
this._dbConfig = null;
|
|
4797
4805
|
this._syncer = null;
|
|
4798
4806
|
this._config = null;
|
|
4807
|
+
this._secrets = null;
|
|
4799
4808
|
}
|
|
4800
4809
|
set apiRootPath(apiRootPath) {
|
|
4801
4810
|
this._apiRootPath = apiRootPath;
|
|
@@ -4836,6 +4845,12 @@ var SonamuClass = class {
|
|
|
4836
4845
|
}
|
|
4837
4846
|
return this._config;
|
|
4838
4847
|
}
|
|
4848
|
+
set secrets(secrets) {
|
|
4849
|
+
this._secrets = secrets;
|
|
4850
|
+
}
|
|
4851
|
+
get secrets() {
|
|
4852
|
+
return this._secrets;
|
|
4853
|
+
}
|
|
4839
4854
|
async init(doSilent = false, enableSync = true, apiRootPath) {
|
|
4840
4855
|
if (this.isInitialized) {
|
|
4841
4856
|
return;
|
|
@@ -4843,12 +4858,18 @@ var SonamuClass = class {
|
|
|
4843
4858
|
!doSilent && console.time(_chalk2.default.cyan("Sonamu.init"));
|
|
4844
4859
|
this.apiRootPath = await _asyncNullishCoalesce(apiRootPath, async () => ( await findApiRootPath()));
|
|
4845
4860
|
const configPath = _path2.default.join(this.apiRootPath, "sonamu.config.json");
|
|
4861
|
+
const secretsPath = _path2.default.join(this.apiRootPath, "sonamu.secrets.json");
|
|
4846
4862
|
if (_fsextra2.default.existsSync(configPath) === false) {
|
|
4847
4863
|
throw new Error(`Cannot find sonamu.config.json in ${configPath}`);
|
|
4848
4864
|
}
|
|
4849
4865
|
this.config = JSON.parse(
|
|
4850
4866
|
_fsextra2.default.readFileSync(configPath).toString()
|
|
4851
4867
|
);
|
|
4868
|
+
if (_fsextra2.default.existsSync(secretsPath)) {
|
|
4869
|
+
this.secrets = JSON.parse(
|
|
4870
|
+
_fsextra2.default.readFileSync(secretsPath).toString()
|
|
4871
|
+
);
|
|
4872
|
+
}
|
|
4852
4873
|
this.dbConfig = await DB.readKnexfile();
|
|
4853
4874
|
!doSilent && console.log(_chalk2.default.green("DB Config Loaded!"));
|
|
4854
4875
|
attachOnDuplicateUpdate();
|
|
@@ -4868,9 +4889,9 @@ var SonamuClass = class {
|
|
|
4868
4889
|
this.isInitialized = true;
|
|
4869
4890
|
!doSilent && console.timeEnd(_chalk2.default.cyan("Sonamu.init"));
|
|
4870
4891
|
}
|
|
4871
|
-
async withFastify(server, config) {
|
|
4892
|
+
async withFastify(server, config, options) {
|
|
4872
4893
|
if (this.isInitialized === false) {
|
|
4873
|
-
await this.init();
|
|
4894
|
+
await this.init(_optionalChain([options, 'optionalAccess', _83 => _83.doSilent]), _optionalChain([options, 'optionalAccess', _84 => _84.enableSync]));
|
|
4874
4895
|
}
|
|
4875
4896
|
server.get(
|
|
4876
4897
|
`${this.config.route.prefix}/routes`,
|
|
@@ -5288,7 +5309,7 @@ var Entity = class {
|
|
|
5288
5309
|
const relSubsetQuery = relEntity.resolveSubsetQuery("", relFields);
|
|
5289
5310
|
let manyJoin;
|
|
5290
5311
|
if (isHasManyRelationProp(relation)) {
|
|
5291
|
-
const fromCol = _nullishCoalesce(_optionalChain([relation, 'optionalAccess',
|
|
5312
|
+
const fromCol = _nullishCoalesce(_optionalChain([relation, 'optionalAccess', _85 => _85.fromColumn]), () => ( "id"));
|
|
5292
5313
|
manyJoin = {
|
|
5293
5314
|
fromTable: this.table,
|
|
5294
5315
|
fromCol,
|
|
@@ -6493,7 +6514,7 @@ ${onlyTs.map((f) => f.name).join("\n")}`
|
|
|
6493
6514
|
const [, keyName, from, referencesTable, referencesField, onClause] = matched2;
|
|
6494
6515
|
const [onUpdateFull, _onUpdate] = _nullishCoalesce((_nullishCoalesce(onClause, () => ( ""))).match(/ON UPDATE ([A-Z ]+)$/), () => ( []));
|
|
6495
6516
|
const onUpdate = _nullishCoalesce(_onUpdate, () => ( "NO ACTION"));
|
|
6496
|
-
const onDelete = _nullishCoalesce(_optionalChain([(_nullishCoalesce(onClause, () => ( ""))), 'access',
|
|
6517
|
+
const onDelete = _nullishCoalesce(_optionalChain([(_nullishCoalesce(onClause, () => ( ""))), 'access', _86 => _86.replace, 'call', _87 => _87(_nullishCoalesce(onUpdateFull, () => ( "")), ""), 'access', _88 => _88.match, 'call', _89 => _89(/ON DELETE ([A-Z ]+) /), 'optionalAccess', _90 => _90[1]]), () => ( "NO ACTION"));
|
|
6497
6518
|
return {
|
|
6498
6519
|
keyName,
|
|
6499
6520
|
from,
|
|
@@ -7177,10 +7198,13 @@ ${onlyTs.map((f) => f.name).join("\n")}`
|
|
|
7177
7198
|
|
|
7178
7199
|
|
|
7179
7200
|
|
|
7201
|
+
|
|
7202
|
+
var _fs = require('fs');
|
|
7180
7203
|
var FixtureManagerClass = class {
|
|
7181
7204
|
constructor() {
|
|
7182
7205
|
this._tdb = null;
|
|
7183
7206
|
this._fdb = null;
|
|
7207
|
+
this.dependencyGraph = /* @__PURE__ */ new Map();
|
|
7184
7208
|
}
|
|
7185
7209
|
set tdb(tdb) {
|
|
7186
7210
|
this._tdb = tdb;
|
|
@@ -7273,6 +7297,9 @@ var FixtureManagerClass = class {
|
|
|
7273
7297
|
await transaction.raw(`SET FOREIGN_KEY_CHECKS = 0`);
|
|
7274
7298
|
await transaction(tableName).truncate();
|
|
7275
7299
|
const rows = await frdb(tableName);
|
|
7300
|
+
if (rows.length === 0) {
|
|
7301
|
+
return;
|
|
7302
|
+
}
|
|
7276
7303
|
console.log(_chalk2.default.blue(tableName), rows.length);
|
|
7277
7304
|
await transaction.insert(
|
|
7278
7305
|
rows.map((row) => {
|
|
@@ -7357,6 +7384,364 @@ var FixtureManagerClass = class {
|
|
|
7357
7384
|
}
|
|
7358
7385
|
await BaseModel.destroy();
|
|
7359
7386
|
}
|
|
7387
|
+
async getFixtures(sourceDBName, targetDBName, searchOptions) {
|
|
7388
|
+
const sourceDB = _knex2.default.call(void 0, Sonamu.dbConfig[sourceDBName]);
|
|
7389
|
+
const targetDB = _knex2.default.call(void 0, Sonamu.dbConfig[targetDBName]);
|
|
7390
|
+
const { entityId, field, value, searchType } = searchOptions;
|
|
7391
|
+
const entity = EntityManager.get(entityId);
|
|
7392
|
+
const column = _optionalChain([entity, 'access', _91 => _91.props, 'access', _92 => _92.find, 'call', _93 => _93((prop) => prop.name === field), 'optionalAccess', _94 => _94.type]) === "relation" ? `${field}_id` : field;
|
|
7393
|
+
let query = sourceDB(entity.table);
|
|
7394
|
+
if (searchType === "equals") {
|
|
7395
|
+
query = query.where(column, value);
|
|
7396
|
+
} else if (searchType === "like") {
|
|
7397
|
+
query = query.where(column, "like", `%${value}%`);
|
|
7398
|
+
}
|
|
7399
|
+
const rows = await query;
|
|
7400
|
+
if (rows.length === 0) {
|
|
7401
|
+
throw new Error("No records found");
|
|
7402
|
+
}
|
|
7403
|
+
const fixtures = [];
|
|
7404
|
+
for (const row of rows) {
|
|
7405
|
+
const initialRecordsLength = fixtures.length;
|
|
7406
|
+
const newRecords = await this.createFixtureRecord(entity, row);
|
|
7407
|
+
fixtures.push(...newRecords);
|
|
7408
|
+
const currentFixtureRecord = fixtures.find(
|
|
7409
|
+
(r) => r.fixtureId === `${entityId}#${row.id}`
|
|
7410
|
+
);
|
|
7411
|
+
if (currentFixtureRecord) {
|
|
7412
|
+
currentFixtureRecord.fetchedRecords = fixtures.filter((r) => r.fixtureId !== currentFixtureRecord.fixtureId).slice(initialRecordsLength).map((r) => r.fixtureId);
|
|
7413
|
+
}
|
|
7414
|
+
}
|
|
7415
|
+
for await (const fixture of fixtures) {
|
|
7416
|
+
const entity2 = EntityManager.get(fixture.entityId);
|
|
7417
|
+
const row = await targetDB(entity2.table).where("id", fixture.id).first();
|
|
7418
|
+
if (row) {
|
|
7419
|
+
const [record] = await this.createFixtureRecord(entity2, row, {
|
|
7420
|
+
singleRecord: true,
|
|
7421
|
+
_db: targetDB
|
|
7422
|
+
});
|
|
7423
|
+
fixture.target = record;
|
|
7424
|
+
continue;
|
|
7425
|
+
}
|
|
7426
|
+
const uniqueRow = await this.checkUniqueViolation(
|
|
7427
|
+
targetDB,
|
|
7428
|
+
entity2,
|
|
7429
|
+
fixture
|
|
7430
|
+
);
|
|
7431
|
+
if (uniqueRow) {
|
|
7432
|
+
const [record] = await this.createFixtureRecord(entity2, uniqueRow, {
|
|
7433
|
+
singleRecord: true,
|
|
7434
|
+
_db: targetDB
|
|
7435
|
+
});
|
|
7436
|
+
fixture.unique = record;
|
|
7437
|
+
}
|
|
7438
|
+
}
|
|
7439
|
+
return fixtures;
|
|
7440
|
+
}
|
|
7441
|
+
async createFixtureRecord(entity, row, options, visitedEntities = /* @__PURE__ */ new Set()) {
|
|
7442
|
+
const fixtureId = `${entity.id}#${row.id}`;
|
|
7443
|
+
if (visitedEntities.has(fixtureId)) {
|
|
7444
|
+
return [];
|
|
7445
|
+
}
|
|
7446
|
+
visitedEntities.add(fixtureId);
|
|
7447
|
+
const records = [];
|
|
7448
|
+
const record = {
|
|
7449
|
+
fixtureId,
|
|
7450
|
+
entityId: entity.id,
|
|
7451
|
+
id: row.id,
|
|
7452
|
+
columns: {},
|
|
7453
|
+
fetchedRecords: [],
|
|
7454
|
+
belongsRecords: []
|
|
7455
|
+
};
|
|
7456
|
+
for (const prop of entity.props) {
|
|
7457
|
+
if (isVirtualProp(prop)) {
|
|
7458
|
+
continue;
|
|
7459
|
+
}
|
|
7460
|
+
record.columns[prop.name] = {
|
|
7461
|
+
prop,
|
|
7462
|
+
value: row[prop.name]
|
|
7463
|
+
};
|
|
7464
|
+
const db = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _95 => _95._db]), () => ( BaseModel.getDB("w")));
|
|
7465
|
+
if (isManyToManyRelationProp(prop)) {
|
|
7466
|
+
const relatedEntity = EntityManager.get(prop.with);
|
|
7467
|
+
const throughTable = prop.joinTable;
|
|
7468
|
+
const fromColumn = `${_inflection2.default.singularize(entity.table)}_id`;
|
|
7469
|
+
const toColumn = `${_inflection2.default.singularize(relatedEntity.table)}_id`;
|
|
7470
|
+
const relatedIds = await db(throughTable).where(fromColumn, row.id).pluck(toColumn);
|
|
7471
|
+
record.columns[prop.name].value = relatedIds;
|
|
7472
|
+
} else if (isHasManyRelationProp(prop)) {
|
|
7473
|
+
const relatedEntity = EntityManager.get(prop.with);
|
|
7474
|
+
const relatedIds = await db(relatedEntity.table).where(prop.joinColumn, row.id).pluck("id");
|
|
7475
|
+
record.columns[prop.name].value = relatedIds;
|
|
7476
|
+
} else if (isOneToOneRelationProp(prop) && !prop.hasJoinColumn) {
|
|
7477
|
+
const relatedEntity = EntityManager.get(prop.with);
|
|
7478
|
+
const relatedProp = relatedEntity.props.find(
|
|
7479
|
+
(p) => p.type === "relation" && p.with === entity.id
|
|
7480
|
+
);
|
|
7481
|
+
if (relatedProp) {
|
|
7482
|
+
const relatedRow = await db(relatedEntity.table).where("id", row.id).first();
|
|
7483
|
+
record.columns[prop.name].value = _optionalChain([relatedRow, 'optionalAccess', _96 => _96.id]);
|
|
7484
|
+
}
|
|
7485
|
+
} else if (isRelationProp(prop)) {
|
|
7486
|
+
const relatedId = row[`${prop.name}_id`];
|
|
7487
|
+
record.columns[prop.name].value = relatedId;
|
|
7488
|
+
if (relatedId) {
|
|
7489
|
+
record.belongsRecords.push(`${prop.with}#${relatedId}`);
|
|
7490
|
+
}
|
|
7491
|
+
if (!_optionalChain([options, 'optionalAccess', _97 => _97.singleRecord]) && relatedId) {
|
|
7492
|
+
const relatedEntity = EntityManager.get(prop.with);
|
|
7493
|
+
const relatedRow = await db(relatedEntity.table).where("id", relatedId).first();
|
|
7494
|
+
if (relatedRow) {
|
|
7495
|
+
const newRecords = await this.createFixtureRecord(
|
|
7496
|
+
relatedEntity,
|
|
7497
|
+
relatedRow,
|
|
7498
|
+
options,
|
|
7499
|
+
visitedEntities
|
|
7500
|
+
);
|
|
7501
|
+
records.push(...newRecords);
|
|
7502
|
+
}
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7506
|
+
records.push(record);
|
|
7507
|
+
return records;
|
|
7508
|
+
}
|
|
7509
|
+
async insertFixtures(dbName, _fixtures) {
|
|
7510
|
+
const fixtures = _lodash2.default.uniqBy(_fixtures, (f) => f.fixtureId);
|
|
7511
|
+
this.buildDependencyGraph(fixtures);
|
|
7512
|
+
const insertionOrder = this.getInsertionOrder();
|
|
7513
|
+
const db = _knex2.default.call(void 0, Sonamu.dbConfig[dbName]);
|
|
7514
|
+
await db.transaction(async (trx) => {
|
|
7515
|
+
await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);
|
|
7516
|
+
for (const fixtureId of insertionOrder) {
|
|
7517
|
+
const fixture = fixtures.find((f) => f.fixtureId === fixtureId);
|
|
7518
|
+
const result = await this.insertFixture(trx, fixture);
|
|
7519
|
+
if (result.id !== fixture.id) {
|
|
7520
|
+
console.log(
|
|
7521
|
+
_chalk2.default.yellow(
|
|
7522
|
+
`Unique constraint violation: ${fixture.entityId}#${fixture.id} -> ${fixture.entityId}#${result.id}`
|
|
7523
|
+
)
|
|
7524
|
+
);
|
|
7525
|
+
fixtures.forEach((f) => {
|
|
7526
|
+
Object.values(f.columns).forEach((column) => {
|
|
7527
|
+
if (column.prop.type === "relation" && column.prop.with === result.entityId && column.value === fixture.id) {
|
|
7528
|
+
column.value = result.id;
|
|
7529
|
+
}
|
|
7530
|
+
});
|
|
7531
|
+
});
|
|
7532
|
+
fixture.id = result.id;
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
for (const fixtureId of insertionOrder) {
|
|
7536
|
+
const fixture = fixtures.find((f) => f.fixtureId === fixtureId);
|
|
7537
|
+
await this.handleManyToManyRelations(trx, fixture, fixtures);
|
|
7538
|
+
}
|
|
7539
|
+
await trx.raw(`SET FOREIGN_KEY_CHECKS = 1`);
|
|
7540
|
+
});
|
|
7541
|
+
const records = [];
|
|
7542
|
+
for await (const r of fixtures) {
|
|
7543
|
+
const entity = EntityManager.get(r.entityId);
|
|
7544
|
+
const record = await db(entity.table).where("id", r.id).first();
|
|
7545
|
+
records.push({
|
|
7546
|
+
entityId: r.entityId,
|
|
7547
|
+
data: record
|
|
7548
|
+
});
|
|
7549
|
+
}
|
|
7550
|
+
return _lodash2.default.uniqBy(records, (r) => `${r.entityId}#${r.data.id}`);
|
|
7551
|
+
}
|
|
7552
|
+
getInsertionOrder() {
|
|
7553
|
+
const visited = /* @__PURE__ */ new Set();
|
|
7554
|
+
const order = [];
|
|
7555
|
+
const tempVisited = /* @__PURE__ */ new Set();
|
|
7556
|
+
const visit = (fixtureId) => {
|
|
7557
|
+
if (visited.has(fixtureId)) return;
|
|
7558
|
+
if (tempVisited.has(fixtureId)) {
|
|
7559
|
+
console.warn(`Circular dependency detected involving: ${fixtureId}`);
|
|
7560
|
+
return;
|
|
7561
|
+
}
|
|
7562
|
+
tempVisited.add(fixtureId);
|
|
7563
|
+
const node = this.dependencyGraph.get(fixtureId);
|
|
7564
|
+
const entity = EntityManager.get(node.entityId);
|
|
7565
|
+
for (const depId of node.dependencies) {
|
|
7566
|
+
const depNode = this.dependencyGraph.get(depId);
|
|
7567
|
+
const relationProp = entity.props.find(
|
|
7568
|
+
(prop) => isRelationProp(prop) && (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) && prop.with === depNode.entityId
|
|
7569
|
+
);
|
|
7570
|
+
if (relationProp && !relationProp.nullable) {
|
|
7571
|
+
visit(depId);
|
|
7572
|
+
}
|
|
7573
|
+
}
|
|
7574
|
+
tempVisited.delete(fixtureId);
|
|
7575
|
+
visited.add(fixtureId);
|
|
7576
|
+
order.push(fixtureId);
|
|
7577
|
+
};
|
|
7578
|
+
for (const fixtureId of this.dependencyGraph.keys()) {
|
|
7579
|
+
visit(fixtureId);
|
|
7580
|
+
}
|
|
7581
|
+
for (const fixtureId of this.dependencyGraph.keys()) {
|
|
7582
|
+
if (!visited.has(fixtureId)) {
|
|
7583
|
+
order.push(fixtureId);
|
|
7584
|
+
}
|
|
7585
|
+
}
|
|
7586
|
+
return order;
|
|
7587
|
+
}
|
|
7588
|
+
prepareInsertData(fixture) {
|
|
7589
|
+
const insertData = {};
|
|
7590
|
+
for (const [propName, column] of Object.entries(fixture.columns)) {
|
|
7591
|
+
if (isVirtualProp(column.prop)) {
|
|
7592
|
+
continue;
|
|
7593
|
+
}
|
|
7594
|
+
const prop = column.prop;
|
|
7595
|
+
if (!isRelationProp(prop)) {
|
|
7596
|
+
if (prop.type === "json") {
|
|
7597
|
+
insertData[propName] = JSON.stringify(column.value);
|
|
7598
|
+
} else {
|
|
7599
|
+
insertData[propName] = column.value;
|
|
7600
|
+
}
|
|
7601
|
+
} else if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) {
|
|
7602
|
+
insertData[`${propName}_id`] = column.value;
|
|
7603
|
+
}
|
|
7604
|
+
}
|
|
7605
|
+
return insertData;
|
|
7606
|
+
}
|
|
7607
|
+
buildDependencyGraph(fixtures) {
|
|
7608
|
+
this.dependencyGraph.clear();
|
|
7609
|
+
for (const fixture of fixtures) {
|
|
7610
|
+
this.dependencyGraph.set(fixture.fixtureId, {
|
|
7611
|
+
fixtureId: fixture.fixtureId,
|
|
7612
|
+
entityId: fixture.entityId,
|
|
7613
|
+
dependencies: /* @__PURE__ */ new Set()
|
|
7614
|
+
});
|
|
7615
|
+
}
|
|
7616
|
+
for (const fixture of fixtures) {
|
|
7617
|
+
const node = this.dependencyGraph.get(fixture.fixtureId);
|
|
7618
|
+
for (const [, column] of Object.entries(fixture.columns)) {
|
|
7619
|
+
const prop = column.prop;
|
|
7620
|
+
if (isRelationProp(prop)) {
|
|
7621
|
+
if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) {
|
|
7622
|
+
const relatedFixtureId = `${prop.with}#${column.value}`;
|
|
7623
|
+
if (this.dependencyGraph.has(relatedFixtureId)) {
|
|
7624
|
+
node.dependencies.add(relatedFixtureId);
|
|
7625
|
+
}
|
|
7626
|
+
} else if (isManyToManyRelationProp(prop)) {
|
|
7627
|
+
const relatedIds = column.value;
|
|
7628
|
+
for (const relatedId of relatedIds) {
|
|
7629
|
+
const relatedFixtureId = `${prop.with}#${relatedId}`;
|
|
7630
|
+
if (this.dependencyGraph.has(relatedFixtureId)) {
|
|
7631
|
+
node.dependencies.add(relatedFixtureId);
|
|
7632
|
+
this.dependencyGraph.get(relatedFixtureId).dependencies.add(fixture.fixtureId);
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
async insertFixture(db, fixture) {
|
|
7641
|
+
const insertData = this.prepareInsertData(fixture);
|
|
7642
|
+
const entity = EntityManager.get(fixture.entityId);
|
|
7643
|
+
try {
|
|
7644
|
+
const uniqueFound = await this.checkUniqueViolation(db, entity, fixture);
|
|
7645
|
+
if (uniqueFound) {
|
|
7646
|
+
return {
|
|
7647
|
+
entityId: fixture.entityId,
|
|
7648
|
+
id: uniqueFound.id
|
|
7649
|
+
};
|
|
7650
|
+
}
|
|
7651
|
+
const found = await db(entity.table).where("id", fixture.id).first();
|
|
7652
|
+
if (found && !fixture.override) {
|
|
7653
|
+
return {
|
|
7654
|
+
entityId: fixture.entityId,
|
|
7655
|
+
id: found.id
|
|
7656
|
+
};
|
|
7657
|
+
}
|
|
7658
|
+
const q = db.insert(insertData).into(entity.table);
|
|
7659
|
+
await q.onDuplicateUpdate.apply(q, Object.keys(insertData));
|
|
7660
|
+
return {
|
|
7661
|
+
entityId: fixture.entityId,
|
|
7662
|
+
id: fixture.id
|
|
7663
|
+
};
|
|
7664
|
+
} catch (err) {
|
|
7665
|
+
console.log(err);
|
|
7666
|
+
throw err;
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
async handleManyToManyRelations(db, fixture, fixtures) {
|
|
7670
|
+
for (const [, column] of Object.entries(fixture.columns)) {
|
|
7671
|
+
const prop = column.prop;
|
|
7672
|
+
if (isManyToManyRelationProp(prop)) {
|
|
7673
|
+
const joinTable = prop.joinTable;
|
|
7674
|
+
const relatedIds = column.value;
|
|
7675
|
+
for (const relatedId of relatedIds) {
|
|
7676
|
+
if (!fixtures.find((f) => f.fixtureId === `${prop.with}#${relatedId}`)) {
|
|
7677
|
+
continue;
|
|
7678
|
+
}
|
|
7679
|
+
const entity = EntityManager.get(fixture.entityId);
|
|
7680
|
+
const relatedEntity = EntityManager.get(prop.with);
|
|
7681
|
+
if (!entity || !relatedEntity) {
|
|
7682
|
+
throw new Error(
|
|
7683
|
+
`Entity not found: ${fixture.entityId}, ${prop.with}`
|
|
7684
|
+
);
|
|
7685
|
+
}
|
|
7686
|
+
const [found] = await db(joinTable).where({
|
|
7687
|
+
[`${_inflection2.default.singularize(entity.table)}_id`]: fixture.id,
|
|
7688
|
+
[`${_inflection2.default.singularize(relatedEntity.table)}_id`]: relatedId
|
|
7689
|
+
}).limit(1);
|
|
7690
|
+
if (found) {
|
|
7691
|
+
continue;
|
|
7692
|
+
}
|
|
7693
|
+
const newIds = await db(joinTable).insert({
|
|
7694
|
+
[`${_inflection2.default.singularize(entity.table)}_id`]: fixture.id,
|
|
7695
|
+
[`${_inflection2.default.singularize(relatedEntity.table)}_id`]: relatedId
|
|
7696
|
+
});
|
|
7697
|
+
console.log(
|
|
7698
|
+
_chalk2.default.green(
|
|
7699
|
+
`Inserted into ${joinTable}: ${entity.table}(${fixture.id}) - ${relatedEntity.table}(${relatedId}) ID: ${newIds}`
|
|
7700
|
+
)
|
|
7701
|
+
);
|
|
7702
|
+
}
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7705
|
+
}
|
|
7706
|
+
async addFixtureLoader(code) {
|
|
7707
|
+
const path8 = Sonamu.apiRootPath + "/src/testing/fixture.ts";
|
|
7708
|
+
let content = _fs.readFileSync.call(void 0, path8).toString();
|
|
7709
|
+
const fixtureLoaderStart = content.indexOf("const fixtureLoader = {");
|
|
7710
|
+
const fixtureLoaderEnd = content.indexOf("};", fixtureLoaderStart);
|
|
7711
|
+
if (fixtureLoaderStart !== -1 && fixtureLoaderEnd !== -1) {
|
|
7712
|
+
const newContent = content.slice(0, fixtureLoaderEnd) + " " + code + "\n" + content.slice(fixtureLoaderEnd);
|
|
7713
|
+
_fs.writeFileSync.call(void 0, path8, newContent);
|
|
7714
|
+
} else {
|
|
7715
|
+
throw new Error("Failed to find fixtureLoader in fixture.ts");
|
|
7716
|
+
}
|
|
7717
|
+
}
|
|
7718
|
+
// 해당 픽스쳐의 값으로 유니크 제약에 위배되는 레코드가 있는지 확인
|
|
7719
|
+
async checkUniqueViolation(db, entity, fixture) {
|
|
7720
|
+
const uniqueIndexes = entity.indexes.filter((i) => i.type === "unique");
|
|
7721
|
+
if (uniqueIndexes.length === 0) {
|
|
7722
|
+
return null;
|
|
7723
|
+
}
|
|
7724
|
+
let uniqueQuery = db(entity.table);
|
|
7725
|
+
for (const index of uniqueIndexes) {
|
|
7726
|
+
if (index.columns.some(
|
|
7727
|
+
(column) => fixture.columns[column.split("_id")[0]].value === null
|
|
7728
|
+
)) {
|
|
7729
|
+
continue;
|
|
7730
|
+
}
|
|
7731
|
+
uniqueQuery = uniqueQuery.orWhere((qb) => {
|
|
7732
|
+
for (const column of index.columns) {
|
|
7733
|
+
const field = column.split("_id")[0];
|
|
7734
|
+
if (Array.isArray(fixture.columns[field].value)) {
|
|
7735
|
+
qb.whereIn(column, fixture.columns[field].value);
|
|
7736
|
+
} else {
|
|
7737
|
+
qb.andWhere(column, fixture.columns[field].value);
|
|
7738
|
+
}
|
|
7739
|
+
}
|
|
7740
|
+
});
|
|
7741
|
+
}
|
|
7742
|
+
const [uniqueFound] = await uniqueQuery;
|
|
7743
|
+
return uniqueFound;
|
|
7744
|
+
}
|
|
7360
7745
|
};
|
|
7361
7746
|
var FixtureManager = new FixtureManagerClass();
|
|
7362
7747
|
|
|
@@ -7443,4 +7828,4 @@ var FixtureManager = new FixtureManagerClass();
|
|
|
7443
7828
|
|
|
7444
7829
|
|
|
7445
7830
|
exports.SQLDateTimeString = SQLDateTimeString; exports.zArrayable = zArrayable; exports.isIntegerProp = isIntegerProp; exports.isBigIntegerProp = isBigIntegerProp; exports.isTextProp = isTextProp; exports.isStringProp = isStringProp; exports.isEnumProp = isEnumProp; exports.isFloatProp = isFloatProp; exports.isDoubleProp = isDoubleProp; exports.isDecimalProp = isDecimalProp; exports.isBooleanProp = isBooleanProp; exports.isDateProp = isDateProp; exports.isDateTimeProp = isDateTimeProp; exports.isTimeProp = isTimeProp; exports.isTimestampProp = isTimestampProp; exports.isJsonProp = isJsonProp; exports.isUuidProp = isUuidProp; exports.isVirtualProp = isVirtualProp; exports.isRelationProp = isRelationProp; exports.isOneToOneRelationProp = isOneToOneRelationProp; exports.isBelongsToOneRelationProp = isBelongsToOneRelationProp; exports.isHasManyRelationProp = isHasManyRelationProp; exports.isManyToManyRelationProp = isManyToManyRelationProp; exports.isCustomJoinClause = isCustomJoinClause; exports.SonamuQueryMode = SonamuQueryMode; exports.isKnexError = isKnexError; exports.ApiParamType = ApiParamType; exports.RenderingNode = RenderingNode; exports.TemplateOptions = TemplateOptions; exports.TemplateKey = TemplateKey; exports.GenerateOptions = GenerateOptions; exports.PathAndCode = PathAndCode; exports.getZodObjectFromApi = getZodObjectFromApi; exports.getZodObjectFromApiParams = getZodObjectFromApiParams; exports.getZodTypeFromApiParamType = getZodTypeFromApiParamType; exports.propNodeToZodTypeDef = propNodeToZodTypeDef; exports.getTextTypeLength = getTextTypeLength; exports.propToZodTypeDef = propToZodTypeDef; exports.zodTypeToZodCode = zodTypeToZodCode; exports.apiParamToTsCode = apiParamToTsCode; exports.apiParamTypeToTsType = apiParamTypeToTsType; exports.unwrapPromiseOnce = unwrapPromiseOnce; exports.serializeZodType = serializeZodType; exports.zodTypeToTsTypeDef = zodTypeToTsTypeDef; exports.registeredApis = registeredApis; exports.api = api; exports.SoException = SoException; exports.isSoException = isSoException; exports.BadRequestException = BadRequestException; exports.UnauthorizedException = UnauthorizedException; exports.NotFoundException = NotFoundException; exports.ServiceUnavailableException = ServiceUnavailableException; exports.InternalServerErrorException = InternalServerErrorException; exports.AlreadyProcessedException = AlreadyProcessedException; exports.DuplicateRowException = DuplicateRowException; exports.TargetNotFoundException = TargetNotFoundException; exports.globAsync = globAsync; exports.importMultiple = importMultiple; exports.findAppRootPath = findAppRootPath; exports.findApiRootPath = findApiRootPath; exports.nonNullable = nonNullable; exports.Entity = Entity; exports.EntityManager = EntityManager; exports.Syncer = Syncer; exports.isLocal = isLocal; exports.isRemote = isRemote; exports.isInDocker = isInDocker; exports.isDaemonServer = isDaemonServer; exports.isDevelopment = isDevelopment; exports.isStaging = isStaging; exports.isProduction = isProduction; exports.isTest = isTest; exports.DB = DB; exports.isRefField = isRefField; exports.UpsertBuilder = UpsertBuilder; exports.BaseModelClass = BaseModelClass; exports.BaseModel = BaseModel; exports.Sonamu = Sonamu; exports.Migrator = Migrator; exports.FixtureManagerClass = FixtureManagerClass; exports.FixtureManager = FixtureManager;
|
|
7446
|
-
//# sourceMappingURL=chunk-
|
|
7831
|
+
//# sourceMappingURL=chunk-HATLA54Z.js.map
|