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.
@@ -842,19 +842,19 @@ function globAsync(pathPattern) {
842
842
  });
843
843
  }
844
844
  async function importMultiple(filePaths, doRefresh = false) {
845
- return Promise.all(
846
- filePaths.map(async (filePath) => {
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
- return {
853
- filePath,
854
- imported
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, id, rows, chunkSize = 50, trx = null) {
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, id);
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, identifier) {
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 === identifier) continue;
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
- rows.push(`WHEN \`${identifier}\` = ? THEN ?`);
4258
- bindings.push(row[identifier], row[key]);
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 whereInIds = data.map((row) => row[identifier]);
4265
- const whereInPlaceholders = whereInIds.map(() => "?").join(", ");
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.toString();
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 uuids = table.rows.map((row) => row.uuid);
4412
- const upsertedRows = await wdb(tableName).select(_lodash2.default.uniq(["uuid", "id", ...extractFields])).whereIn("uuid", uuids);
4413
- const uuidMap = new Map(
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 ids = Array.from(uuidMap.values()).map((val) => val.id);
4435
- if (groups.selfRef) {
4436
- const selfRefIds = await this.upsert(wdb, tableName);
4437
- return [...ids, ...selfRefIds];
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 ids;
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', _83 => _83.fromColumn]), () => ( "id"));
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', _84 => _84.replace, 'call', _85 => _85(_nullishCoalesce(onUpdateFull, () => ( "")), ""), 'access', _86 => _86.match, 'call', _87 => _87(/ON DELETE ([A-Z ]+) /), 'optionalAccess', _88 => _88[1]]), () => ( "NO ACTION"));
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-5VT5JTY4.js.map
7831
+ //# sourceMappingURL=chunk-HATLA54Z.js.map