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 +1 -1
- package/src/crud/entity-api-generator.js +83 -98
package/package.json
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
505
|
+
await db.collection(collection_name).updateOne(f, { $set: { remove: true } });
|
|
452
506
|
else
|
|
453
|
-
await db.collection(
|
|
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(
|
|
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(
|
|
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
|
}
|