yaml-admin-api 0.0.76 → 0.0.78

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml-admin-api",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
4
4
  "license": "MIT",
5
5
  "description": "YAML Admin API package",
6
6
  "type": "commonjs",
@@ -19,6 +19,19 @@ const asyncErrorHandler = (fn) => (req, res, next) => {
19
19
 
20
20
  const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options }) => {
21
21
 
22
+ // entity 속성이 있으면 해당 값을 실제 collection 이름으로 사용
23
+ const collection_name = yml_entity.entity || entity_name
24
+
25
+ // filter 배열을 객체로 변환
26
+ let default_filter = {}
27
+ if (Array.isArray(yml_entity.filter)) {
28
+ yml_entity.filter.forEach(f => {
29
+ default_filter[f.name] = f.value
30
+ })
31
+ } else if (yml_entity.filter) {
32
+ default_filter = yml_entity.filter
33
+ }
34
+
22
35
  const auth = withConfig({ db, jwt_secret: yml.login["jwt-secret"] });
23
36
  const passwordEncoding = yml.login['password-encoding']
24
37
  const api_host = yml["api-host"].uri;
@@ -76,6 +89,32 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
76
89
  return key
77
90
  }
78
91
 
92
+ // unique 검사 함수
93
+ const checkUnique = async (entity, excludeId = null) => {
94
+ const uniqueFields = yml_entity.unique
95
+ if (!uniqueFields || uniqueFields.length === 0) return
96
+
97
+ const f = {}
98
+ for (const field of uniqueFields) {
99
+ f[field.name] = entity[field.name]
100
+ }
101
+
102
+ // 자기 자신 제외 (update 시)
103
+ if (excludeId !== null) {
104
+ f[key_field.name] = { $ne: excludeId }
105
+ }
106
+
107
+ const existing = await db.collection(collection_name).findOne(f)
108
+ if (existing) {
109
+ const fieldNames = uniqueFields.map(u => {
110
+ const fieldDef = yml_entity.fields?.find(field => field.name === u.name)
111
+ return fieldDef?.label || u.name
112
+ }).join(', ')
113
+ const fieldValues = uniqueFields.map(u => entity[u.name]).join(', ')
114
+ throw new Error("duplicate key of [" + fieldNames + "] - [" + fieldValues + "]")
115
+ }
116
+ }
117
+
79
118
  const parseValueByType = (value, field) => {
80
119
  const { type, reference_entity, reference_match } = field
81
120
  if(value == 'not null')
@@ -177,7 +216,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
177
216
  * @param {*} entity_name
178
217
  */
179
218
  const recalcurateAutoGenerateIndex = async (db, entity_name) => {
180
- const list = await db.collection(entity_name).find({})
219
+ const list = await db.collection(collection_name).find({})
181
220
  .project({ [key_field.name]: 1, _id: 0 })
182
221
  .sort({ [key_field.name]: -1 }).limit(1).toArray()
183
222
  const counter = await db.collection('counters').findOne({ _id: entity_name })
@@ -241,28 +280,31 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
241
280
  f['name'] = { $regex: ".*" + name + ".*" };
242
281
  f.remove = { $ne: true }
243
282
 
283
+ // default_filter 적용
284
+ Object.assign(f, default_filter)
285
+
244
286
  //Custom f list Start
245
287
 
246
288
  //Custom f list End
247
289
 
248
290
  const projection = (key_field.name == '_id' ? {} : { _id: false })
249
- if(yml.debug)
250
- console.log('list', entity_name, f)
251
-
252
291
  let list, count;
253
292
  let aggregate = await makeApiGenerateAggregate(db, entity_name, yml_entity, yml, options)
254
293
 
255
294
  if(aggregate?.length > 0) {
256
295
  aggregate = [...aggregate, {$match: f}]
257
- const countResult = await db.collection(entity_name).aggregate([...aggregate, { $count: 'count' }]).toArray()
296
+ const countResult = await db.collection(collection_name).aggregate([...aggregate, { $count: 'count' }]).toArray()
258
297
  count = countResult.length > 0 ? countResult[0].count : 0
259
298
 
260
299
  aggregate = [...aggregate, { $sort: s }, { $skip: parseInt(_start) }, { $limit: l }]
261
- list = await db.collection(entity_name).aggregate(aggregate).toArray()
262
- } else
263
- {
264
- count = await db.collection(entity_name).find(f).project(projection).sort(s).count()
265
- list = await db.collection(entity_name).find(f).project(projection).sort(s).skip(parseInt(_start)).limit(l).toArray()
300
+ if(yml.debug)
301
+ console.log('list', entity_name, JSON.stringify(aggregate, null, 2))
302
+ list = await db.collection(collection_name).aggregate(aggregate).toArray()
303
+ } else {
304
+ if(yml.debug)
305
+ console.log('list', entity_name, f)
306
+ count = await db.collection(collection_name).find(f).project(projection).sort(s).count()
307
+ list = await db.collection(collection_name).find(f).project(projection).sort(s).skip(parseInt(_start)).limit(l).toArray()
266
308
  }
267
309
 
268
310
  if(yml.debug)
@@ -334,7 +376,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
334
376
  if (entityId) {
335
377
  let f = {}
336
378
  f[key_field.name] = entityId
337
- let already = await db.collection(entity_name).findOne(f)
379
+ let already = await db.collection(collection_name).findOne(f)
338
380
  if (already)
339
381
  throw new Error("duplicate key of [" + key_field.name + "] - [" + entityId + "]")
340
382
  }
@@ -343,11 +385,17 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
343
385
  entity['update_date'] = entity['create_date'] = new Date()
344
386
  entity['create_admin_id'] = req.user.id
345
387
 
388
+ // default_filter 값을 entity에 자동 적용
389
+ Object.assign(entity, default_filter)
390
+
391
+ // unique 검사
392
+ await checkUnique(entity)
393
+
346
394
  //Custom Create Start
347
395
 
348
396
  //Custom Create End
349
397
 
350
- var r = await db.collection(entity_name).insertOne(entity);
398
+ var r = await db.collection(collection_name).insertOne(entity);
351
399
  //Custom Create Tail Start
352
400
  if(options?.listener?.entityCreated)
353
401
  await options.listener.entityCreated(db, entity_name, entity)
@@ -377,6 +425,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
377
425
 
378
426
  let f = {}
379
427
  f[key_field.name] = entityId
428
+ Object.assign(f, default_filter)
380
429
 
381
430
  for (let field of yml_entity.fields) {
382
431
  if (['mp4', 'image', 'file'].includes(field.type)) {
@@ -388,7 +437,10 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
388
437
  }
389
438
  }
390
439
 
391
- await db.collection(entity_name).updateOne(f, { $set: entity });
440
+ // unique 검사 (자기 자신 제외)
441
+ await checkUnique(entity, entityId)
442
+
443
+ await db.collection(collection_name).updateOne(f, { $set: entity });
392
444
 
393
445
  //Custom Create Tail Start
394
446
  if(options?.listener?.entityUpdated)
@@ -405,16 +457,17 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
405
457
  app.get(`${api_prefix}/${entity_name}/:id`, auth.isAuthenticated, asyncErrorHandler(async (req, res) => {
406
458
  let f = {}
407
459
  f[key_field.name] = parseKey(req.params.id)
460
+ Object.assign(f, default_filter)
408
461
 
409
462
  let aggregate = await makeApiGenerateAggregate(db, entity_name, yml_entity, yml, options)
410
463
 
411
464
  let m
412
465
  if(aggregate?.length > 0) {
413
466
  aggregate = [{$match: f}, ...aggregate, { $limit: 1 }]
414
- const result = await db.collection(entity_name).aggregate(aggregate).toArray()
467
+ const result = await db.collection(collection_name).aggregate(aggregate).toArray()
415
468
  m = result.length > 0 ? result[0] : null
416
469
  } else {
417
- m = await db.collection(entity_name).findOne(f);
470
+ m = await db.collection(collection_name).findOne(f);
418
471
  }
419
472
 
420
473
  if (!m)
@@ -433,7 +486,8 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
433
486
  app.delete(`${api_prefix}/${entity_name}/:id`, auth.isAuthenticated, asyncErrorHandler(async (req, res) =>{
434
487
  let f = {}
435
488
  f[key_field.name] = parseKey(req.params.id)
436
- const entity = await db.collection(entity_name).findOne(f);
489
+ Object.assign(f, default_filter)
490
+ const entity = await db.collection(collection_name).findOne(f);
437
491
  if (!entity)
438
492
  return res.status(404).send('Not found');
439
493
 
@@ -448,9 +502,9 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
448
502
  if (customDelete)
449
503
  ;
450
504
  else if (softDelete)
451
- await db.collection(entity_name).updateOne(f, { $set: { remove: true } });
505
+ await db.collection(collection_name).updateOne(f, { $set: { remove: true } });
452
506
  else
453
- await db.collection(entity_name).deleteOne(f);
507
+ await db.collection(collection_name).deleteOne(f);
454
508
 
455
509
  if(options?.listener?.entityDeleted)
456
510
  await options.listener.entityDeleted(db, entity_name, entity)
@@ -469,7 +523,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
469
523
  //{ label: '상품', value: row => row.product_list?.map(m=>m.total_name).join(',') },
470
524
 
471
525
  let f = req.body.filter || {}
472
- const list = await db.collection(entity_name).find(f).project({
526
+ const list = await db.collection(collection_name).find(f).project({
473
527
  _id: false,
474
528
  }).toArray();
475
529
 
@@ -564,7 +618,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
564
618
  })
565
619
  }
566
620
 
567
- let result = await db.collection(entity_name).bulkWrite(bulk);
621
+ let result = await db.collection(collection_name).bulkWrite(bulk);
568
622
  //result에서 update entity와 created entity list로 추출 해서 options?.listener?.entityCreated?.(entity_name, createdEntity)와 options?.listener?.entityUpdated?.(entity_name, updateEntity) 호출
569
623
  try {
570
624
  const upsertIndexToId = new Map()
@@ -704,6 +758,15 @@ const makeApiGenerateAggregate = async (db, entity_name, yml_entity, yml, option
704
758
  { $match: { $expr: { $eq: ['$' + match, '$$local_key'] } } }
705
759
  ]
706
760
 
761
+ // filter 처리
762
+ if (Array.isArray(apiGenerateItem.filter)) {
763
+ let filterMatch = {}
764
+ apiGenerateItem.filter.forEach(f => {
765
+ filterMatch[f.name] = f.value
766
+ })
767
+ innerPipeline.push({ $match: filterMatch })
768
+ }
769
+
707
770
  // single일 때 max _id로 하나만 선택 ($match 바로 다음에)
708
771
  if(single) {
709
772
  innerPipeline.push({ $sort: { _id: -1 } })
@@ -763,84 +826,6 @@ const makeApiGenerateAggregate = async (db, entity_name, yml_entity, yml, option
763
826
  return aggregate
764
827
  }
765
828
 
766
- const makeApiGenerateFields = async (db, entity_name, yml_entity, yml, options, data_list) => {
767
- const apiGenerate = yml_entity.api_generate
768
- if(!apiGenerate)
769
- return;
770
- for(let key in apiGenerate) {
771
-
772
- const apiGenerateItem = apiGenerate[key]
773
- let { entity, field, fields, match, sort, limit, single, match_from } = apiGenerateItem
774
-
775
- sort = sort || []
776
- sort = makeMongoSortFromYml(sort)
777
- limit = limit || 1000
778
-
779
- let match_from_list = data_list.map(m=>matchPathInObject(m, match_from))
780
- match_from_list = match_from_list.filter(m=>m)
781
- const projection = {[match]:1}
782
-
783
- const aggregate = [
784
- { $match: { [match]: {$in:match_from_list} } },
785
- ]
786
-
787
- if(field)
788
- projection[field] = 1
789
- else if(fields){
790
- fields.map(m=>{
791
- projection[m.name] = 1
792
- })
793
-
794
- fields.map(m=>{
795
- if(m.type == 'reference') {
796
- let project = { _id: 0 }
797
-
798
- if(m.field)
799
- project[m.field] = 1
800
- else
801
- m.fields.map(f=>{
802
- project[f.name] = 1
803
- })
804
-
805
- aggregate.push({ $lookup: {
806
- from: m.reference_entity,
807
- let: { local_key: '$'+m.reference_from },
808
- pipeline: [
809
- { $match: { $expr: { $eq: ["$"+m.reference_match, "$$local_key"] } } },
810
- { $project: project }
811
- ],
812
- as: m.name
813
- } })
814
- if(m.single)
815
- aggregate.push({ $unwind: `$${m.name}` })
816
-
817
- if(m.field)
818
- aggregate.push({ $addFields: { [m.field]: `$${m.name}.${m.field}` } })
819
- }
820
- })
821
- }
822
-
823
- const result = await db.collection(entity)
824
- .aggregate(aggregate)
825
- .project(projection)
826
- .toArray()
827
- data_list.map(m=>{
828
- let found = result.filter(f=>matchPathInObject(f, match) === matchPathInObject(m, match_from))
829
- if(single) {
830
- if(field)
831
- m[key] = found.length > 0 ? found[0][field] : null
832
- else
833
- m[key] = found.length > 0 ? found[0] : null
834
- } else {
835
- if(field)
836
- m[key] = found.map(f=>f[field])
837
- else
838
- m[key] = found
839
- }
840
- })
841
- }
842
- }
843
-
844
829
  const generateEntityApi = async ({ app, db, entity_name, entity, yml, options }) => {
845
830
  await generateCrud({ app, db, entity_name, yml_entity: entity, yml, options })
846
831
  }