yaml-admin-api 0.0.69 → 0.0.71

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.69",
3
+ "version": "0.0.71",
4
4
  "license": "MIT",
5
5
  "description": "YAML Admin API package",
6
6
  "type": "commonjs",
@@ -82,7 +82,15 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
82
82
  return { $ne: null }
83
83
  else if(value == 'null')
84
84
  return null
85
- if (type == 'reference') {
85
+ if (type == 'boolean') {
86
+ if(value == 'true')
87
+ return true
88
+ else if(value == 'false')
89
+ return false
90
+ else
91
+ return null
92
+ }
93
+ else if (type == 'reference') {
86
94
  const referenceEntity = yml.entity[reference_entity]
87
95
  const referenceField = referenceEntity.fields.find(f => f.name == reference_match)
88
96
  if(!referenceField)
@@ -131,7 +139,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
131
139
  }
132
140
  }
133
141
 
134
- let apiGenerateFields = await makeApiGenerateFields(db, entity_name, yml_entity, yml, options, list)
142
+ //await makeApiGenerateFields(db, entity_name, yml_entity, yml, options, list)
135
143
  }
136
144
 
137
145
  const mediaKeyToFullUrl = async (key, private) => {
@@ -189,10 +197,12 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
189
197
  var _order = req.query._order;
190
198
  if (_sort != null)
191
199
  s[_sort] = (_order == 'ASC' ? 1 : -1);
200
+ else
201
+ s._id = -1
192
202
 
193
203
  var _end = req.query._end;
194
- var _start = req.query._start;
195
- var l = _end - _start;
204
+ var _start = req.query._start || 0;
205
+ var l = _end - _start || 50;
196
206
 
197
207
  //검색 파라미터
198
208
  const f = {};
@@ -238,13 +248,36 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
238
248
  const projection = (key_field.name == '_id' ? {} : { _id: false })
239
249
  if(yml.debug)
240
250
  console.log('list', entity_name, f)
241
- var count = await db.collection(entity_name).find(f).project(projection).sort(s).count();
242
- let list = await db.collection(entity_name).find(f).project(projection).sort(s).skip(parseInt(_start)).limit(l).toArray()
251
+
252
+ let list, count;
253
+ let aggregate = await makeApiGenerateAggregate(db, entity_name, yml_entity, yml, options)
254
+
255
+ if(aggregate?.length > 0) {
256
+ aggregate = [{$match: f}, ...aggregate]
257
+
258
+ const countResult = await db.collection(entity_name).aggregate([...aggregate, { $count: 'count' }]).toArray()
259
+ count = countResult.length > 0 ? countResult[0].count : 0
260
+
261
+ list = await db.collection(entity_name).aggregate(aggregate)
262
+ .sort(s)
263
+ .skip(parseInt(_start))
264
+ .limit(l).toArray()
265
+ } else
266
+ {
267
+ count = await db.collection(entity_name).find(f).project(projection).sort(s).count()
268
+ list = await db.collection(entity_name).find(f).project(projection).sort(s).skip(parseInt(_start)).limit(l).toArray()
269
+ }
270
+
271
+ if(yml.debug)
272
+ console.log('list', entity_name, 'count', count, 'list length', list.length)
273
+
243
274
  list.map(m => {
244
275
  m.id = getKeyFromEntity(m)
245
276
  })
246
277
 
247
278
  await addInfo(db, list)
279
+ //await makeApiGenerateFields(db, entity_name, yml_entity, yml, options, list)
280
+
248
281
  if(options?.listener?.entityListed)
249
282
  await options.listener.entityListed(db, entity_name, list)
250
283
 
@@ -375,7 +408,18 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
375
408
  app.get(`${api_prefix}/${entity_name}/:id`, auth.isAuthenticated, asyncErrorHandler(async (req, res) => {
376
409
  let f = {}
377
410
  f[key_field.name] = parseKey(req.params.id)
378
- const m = await db.collection(entity_name).findOne(f);
411
+
412
+ let aggregate = await makeApiGenerateAggregate(db, entity_name, yml_entity, yml, options)
413
+
414
+ let m
415
+ if(aggregate?.length > 0) {
416
+ aggregate = [{$match: f}, ...aggregate, { $limit: 1 }]
417
+ const result = await db.collection(entity_name).aggregate(aggregate).toArray()
418
+ m = result.length > 0 ? result[0] : null
419
+ } else {
420
+ m = await db.collection(entity_name).findOne(f);
421
+ }
422
+
379
423
  if (!m)
380
424
  return res.status(404).send('Not found');
381
425
 
@@ -384,7 +428,7 @@ const generateCrud = async ({ app, db, entity_name, yml_entity, yml, options })
384
428
 
385
429
  if(yml.debug)
386
430
  console.log('show', entity_name, m)
387
-
431
+
388
432
  res.json(m);
389
433
  }))
390
434
 
@@ -602,6 +646,120 @@ const matchPathInObject = (obj, path) => {
602
646
  return r
603
647
  }
604
648
 
649
+ const makeApiGenerateAggregate = async (db, entity_name, yml_entity, yml, options) => {
650
+ const apiGenerate = yml_entity.api_generate
651
+ if(!apiGenerate)
652
+ return;
653
+
654
+ // reference 필드를 위한 중첩 pipeline 생성 함수
655
+ const buildReferencePipeline = (refField) => {
656
+ const pipeline = []
657
+ let project = { _id: 0 }
658
+
659
+ if(refField.field)
660
+ project[refField.field] = 1
661
+ else if(refField.fields) {
662
+ refField.fields.forEach(f => {
663
+ project[f.name] = 1
664
+ })
665
+ }
666
+
667
+ pipeline.push({
668
+ $lookup: {
669
+ from: refField.reference_entity,
670
+ localField: refField.reference_from,
671
+ foreignField: refField.reference_match,
672
+ pipeline: [{ $project: project }],
673
+ as: refField.name
674
+ }
675
+ })
676
+
677
+ if(refField.single) {
678
+ pipeline.push({
679
+ $unwind: {
680
+ path: `$${refField.name}`,
681
+ preserveNullAndEmptyArrays: true
682
+ }
683
+ })
684
+ }
685
+
686
+ if(refField.field) {
687
+ pipeline.push({
688
+ $addFields: { [refField.name]: `$${refField.name}.${refField.field}` }
689
+ })
690
+ }
691
+
692
+ return pipeline
693
+ }
694
+
695
+ const aggregate = []
696
+ for(let key in apiGenerate) {
697
+
698
+ const apiGenerateItem = apiGenerate[key]
699
+ let { entity, field, fields, match, sort, limit, single, match_from } = apiGenerateItem
700
+
701
+ sort = sort || []
702
+ sort = makeMongoSortFromYml(sort)
703
+ limit = limit || 1000
704
+
705
+ // lookup 내부 pipeline 구성
706
+ const innerPipeline = [
707
+ { $match: { $expr: { $eq: ['$' + match, '$$local_key'] } } }
708
+ ]
709
+
710
+ // projection 구성
711
+ const projection = { _id: 0, [match]: 1 }
712
+ if(field) {
713
+ projection[field] = 1
714
+ } else if(fields) {
715
+ fields.forEach(m => {
716
+ if(m.type !== 'reference') {
717
+ projection[m.name] = 1
718
+ }
719
+ })
720
+
721
+ // reference 필드는 중첩 lookup으로 처리
722
+ fields.filter(m => m.type === 'reference').forEach(refField => {
723
+ projection[refField.reference_from] = 1
724
+ const refPipeline = buildReferencePipeline(refField)
725
+ innerPipeline.push(...refPipeline)
726
+ })
727
+ }
728
+
729
+ // projection을 innerPipeline 맨 앞에 추가 (match 다음)
730
+ innerPipeline.splice(1, 0, { $project: projection })
731
+
732
+ // 기본 $lookup 추가
733
+ aggregate.push({
734
+ $lookup: {
735
+ from: entity,
736
+ let: { local_key: '$' + match_from },
737
+ pipeline: innerPipeline,
738
+ as: key
739
+ }
740
+ })
741
+
742
+ if(single) {
743
+ aggregate.push({
744
+ $unwind: {
745
+ path: `$${key}`,
746
+ preserveNullAndEmptyArrays: true
747
+ }
748
+ })
749
+
750
+ // field만 있는 경우 값만 추출
751
+ if(field) {
752
+ aggregate.push({
753
+ $addFields: { [key]: `$${key}.${field}` }
754
+ })
755
+ }
756
+ }
757
+ }
758
+
759
+ //console.log('aggregate', JSON.stringify(aggregate, null, 2))
760
+ return aggregate
761
+ }
762
+
605
763
  const makeApiGenerateFields = async (db, entity_name, yml_entity, yml, options, data_list) => {
606
764
  const apiGenerate = yml_entity.api_generate
607
765
  if(!apiGenerate)