hola-server 0.3.18 → 0.3.21

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/core/meta.js CHANGED
@@ -12,8 +12,10 @@ const meta_manager = {};
12
12
  * create is false, this attribute can be shown in property list but sys property can't be shown in property list
13
13
  *
14
14
  * routes: configure customer defined routes
15
+ * link property: field link property link to entity field and the field should ref to an entity.
16
+ * and the field name should be the same with the ref entity field name and shouldn't make as required and no other property
15
17
  */
16
- const field_attrs = ["name", "type", "required", "ref", "delete", "create", "list", "search", "update", "clone", "sys"];
18
+ const field_attrs = ["name", "type", "required", "ref", "link", "delete", "create", "list", "search", "update", "clone", "sys"];
17
19
  const meta_attrs = ["collection", "primary_keys", "fields", "creatable", "readable", "updatable", "deleteable", "cloneable", "after_read",
18
20
  "before_create", "after_create", "before_clone", "after_clone", "before_update", "after_update", "before_delete", "after_delete", "create", "clone", "update", "batch_update", "after_batch_update", "delete",
19
21
  "ref_label", "ref_filter", "route", "user_field"];
@@ -37,14 +39,16 @@ const validate_field = (meta, field) => {
37
39
  if (field.type) {
38
40
  get_type(field.type);
39
41
  } else {
40
- field.type = "string";
42
+ if (!field.link) {
43
+ field.type = "string"
44
+ }
41
45
  }
42
46
 
43
47
  if (meta.primary_keys.includes(field.name)) {
44
48
  field.required = true;
45
49
  }
46
50
 
47
- if (field.ref) {
51
+ if (field.ref && !field.link) {
48
52
  const ref_meta = meta_manager[field.ref];
49
53
  if (!ref_meta) {
50
54
  throw new Error("meta:" + meta.collection + ",field:" + field.name + " refers invalid meta:" + field.ref + "]");
@@ -71,6 +75,16 @@ const validate_field = (meta, field) => {
71
75
  }
72
76
  }
73
77
 
78
+ if (field.link) {
79
+ const keys = Object.keys(field);
80
+ const support_keys_for_links = ["name", "link", "list"]
81
+ keys.forEach(key => {
82
+ if (!support_keys_for_links.includes(key)) {
83
+ throw new Error("Link field just supports name, link,list property. The attribute [" + key + "] isn't supported for LINK field:" + JSON.stringify(field) + " and meta:" + meta.collection);
84
+ }
85
+ });
86
+ }
87
+
74
88
  const keys = Object.keys(field);
75
89
  keys.forEach(key => {
76
90
  if (!field_attrs.includes(key)) {
@@ -85,13 +99,35 @@ const validate_field = (meta, field) => {
85
99
  * @param {meta fields} fields
86
100
  */
87
101
  const validate_fields = (meta, fields) => {
88
- const field_names = [];
102
+ const fields_map = fields.reduce((map, field) => { map[field.name] = field; return map; }, {});
103
+ const check_duplicate_field_names = [];
104
+
89
105
  fields.forEach(field => {
90
106
  validate_field(meta, field);
91
- if (field_names.includes(field.name)) {
107
+ if (check_duplicate_field_names.includes(field.name)) {
92
108
  throw new Error("Duplicate field defined [" + JSON.stringify(field) + "] for meta:" + meta.collection);
93
109
  } else {
94
- field_names.push(field.name);
110
+ check_duplicate_field_names.push(field.name);
111
+ }
112
+ if (field.link) {
113
+ const link_field = fields_map[field.link];
114
+ if (!link_field) {
115
+ throw new Error("link field [" + JSON.stringify(field) + "] should link to one field defined in meta:" + meta.collection);
116
+ } else {
117
+ if (!link_field.ref) {
118
+ throw new Error("link field [" + JSON.stringify(field) + "] link to field [" + JSON.stringify(link_field) + "] should ref to one entity in meta:" + meta.collection);
119
+ }
120
+ const entity = get_entity_meta(link_field.ref);
121
+ const link_entity_field = entity.fields_map[field.name];
122
+ if (!link_entity_field) {
123
+ throw new Error("link field [" + JSON.stringify(field) + "] should link to one field defined in meta:" + entity.collection);
124
+ }
125
+ //set type to link field type
126
+ field.type = link_entity_field.type;
127
+ if (link_entity_field.ref) {
128
+ field.ref = link_entity_field.ref;
129
+ }
130
+ }
95
131
  }
96
132
  });
97
133
 
@@ -167,22 +203,24 @@ class EntityMeta {
167
203
  this.ref_fields = this.meta.fields.filter(field => field.ref);
168
204
  this.ref_by_metas = [];
169
205
 
206
+ this.link_fields = this.meta.fields.filter(field => field.link);
207
+ this.fields_map = meta.fields.reduce((map, field) => { map[field.name] = field; return map; }, {});
170
208
  this.fields = meta.fields;
171
209
  this.primary_keys = meta.primary_keys;
172
210
  this.field_names = this.fields.map(field => field.name);
173
211
  this.user_field = meta.user_field;
174
212
 
175
213
  this.property_fields = this.fields.filter(field => field.sys != true);
176
- this.create_fields = this.fields.filter(field => field.create != false && field.sys != true);
177
- this.update_fields = this.fields.filter(field => field.create != false && field.update != false && field.sys != true);
178
- this.search_fields = this.fields.filter(field => field.search != false && field.sys != true);
179
- this.clone_fields = this.fields.filter(field => field.clone != false && field.sys != true);
214
+ this.create_fields = this.fields.filter(field => field.create != false && field.sys != true && !field.link);
215
+ this.update_fields = this.fields.filter(field => field.create != false && field.update != false && field.sys != true && !field.link);
216
+ this.search_fields = this.fields.filter(field => field.search != false && field.sys != true && !field.link);
217
+ this.clone_fields = this.fields.filter(field => field.clone != false && field.sys != true && !field.link);
180
218
  this.list_fields = this.fields.filter(field => field.list != false && field.sys != true);
181
219
  this.primary_key_fields = this.fields.filter(field => meta.primary_keys.includes(field.name));
182
220
  this.required_field_names = this.fields.filter(field => field.required == true || this.primary_keys.includes(field.name)).map(field => field.name);
183
221
 
184
222
  this.file_fields = meta.fields.filter(f => f.type === 'file');
185
- this.upload_fields = this.file_fields && this.file_fields.length > 0 ? this.file_fields.map(f => ({ name: f.name, maxCount: f.max ? f.max : 1 })) : [];
223
+ this.upload_fields = this.file_fields && this.file_fields.length > 0 ? this.file_fields.map(f => ({ name: f.name })) : [];
186
224
 
187
225
  set_callback(this, "after_read", meta.after_read);
188
226
  set_callback(this, "before_create", meta.before_create);
package/db/entity.js CHANGED
@@ -180,10 +180,20 @@ class Entity {
180
180
  });
181
181
 
182
182
  const list_field_names = this.meta.list_fields.map(f => f.name);
183
+ const ref_fields = [];
184
+ const link_fields = [];
185
+
183
186
  const attrs = {};
184
- attr_names.split(",").forEach(function (attr) {
187
+ attr_names.split(",").forEach((attr) => {
185
188
  if (list_field_names.includes(attr)) {
186
189
  attrs[attr] = 1;
190
+ const field = this.meta.fields_map[attr];
191
+ if (field.link) {
192
+ link_fields.push(field);
193
+ attrs[field.link] = 1;
194
+ } else if (field.ref) {
195
+ ref_fields.push(field);
196
+ }
187
197
  }
188
198
  });
189
199
 
@@ -203,7 +213,8 @@ class Entity {
203
213
 
204
214
  const total = await this.count(search_query);
205
215
  const list = await this.find_page(search_query, sort, page_int, page_limit, attrs);
206
- const data = await this.convert_ref_attrs(list);
216
+ const list_link = await this.read_link_attrs(list, link_fields);
217
+ const data = await this.convert_ref_attrs(list_link, ref_fields);
207
218
 
208
219
  if (is_log_debug()) {
209
220
  log_debug(LOG_ENTITY, "total:" + total + ",data:" + JSON.stringify(data));
@@ -520,17 +531,18 @@ class Entity {
520
531
  }
521
532
 
522
533
  /**
523
- * Validate the param object and invoke the logic to read entity
524
- * this is used for update entity
534
+ * Use objectid to read entity properties. Validate the param object and invoke the logic to read entity properties.
535
+ * This method doesn't convert ref property, so all the ref properties are objectid of the ref entity.
536
+ * It also donesn't inclue link property. This is used for form view to do create/update the entity.
525
537
  * @param {object id of the entity} _id object id of the entity
526
538
  * @param {attr names to retrieve} attr_names
527
539
  *
528
540
  */
529
- async read_entity(_id, attr_names) {
541
+ async read_property(_id, attr_names) {
530
542
  const query = oid_query(_id);
531
543
  if (query == null) {
532
544
  if (is_log_error()) {
533
- log_error(LOG_ENTITY, "read_entity invalid id:" + _id);
545
+ log_error(LOG_ENTITY, "read_property invalid id:" + _id);
534
546
  }
535
547
  return { code: INVALID_PARAMS, err: ["_id"] };
536
548
  }
@@ -546,7 +558,7 @@ class Entity {
546
558
  const results = await this.find(query, attrs);
547
559
  if (results && results.length == 1) {
548
560
  if (is_log_debug()) {
549
- log_debug("read entity with query:" + JSON.stringify(query) + ",attrs:" + JSON.stringify(attrs) + ",result:" + JSON.stringify(results));
561
+ log_debug("read_property with query:" + JSON.stringify(query) + ",attrs:" + JSON.stringify(attrs) + ",result:" + JSON.stringify(results));
550
562
  }
551
563
  return { code: SUCCESS, data: results[0] };
552
564
  } else {
@@ -555,33 +567,42 @@ class Entity {
555
567
  }
556
568
 
557
569
  /**
558
- * Validate the param object and invoke the logic to read entity properties
559
- * this is used for update entity
560
- * @param {object id of the entity} _id object id of the entity
561
- * @param {attr names to retrieve} attr_names
562
- *
563
- */
564
- async read_entity_properties(_id, attr_names) {
570
+ * Use objectid to read entity properties. Validate the param object and invoke the logic to read entity properties.
571
+ * It will convert object ref attributes to ref_label property of the ref entity and also read link attributes.
572
+ * @param {object id of the entity} _id object id of the entity
573
+ * @param {attr names to retrieve} attr_names
574
+ *
575
+ */
576
+ async read_entity(_id, attr_names) {
565
577
  const query = oid_query(_id);
566
578
  if (query == null) {
567
579
  if (is_log_error()) {
568
- log_error(LOG_ENTITY, "read_entity_properties invalid id:" + _id);
580
+ log_error(LOG_ENTITY, "read_entity invalid id:" + _id);
569
581
  }
570
582
  return { code: INVALID_PARAMS, err: ["_id"] };
571
583
  }
572
584
 
573
585
  if (!attr_names) {
574
586
  if (is_log_error()) {
575
- log_error(LOG_ENTITY, "read_entity_properties invalid attr_names:" + attr_names);
587
+ log_error(LOG_ENTITY, "read_entity invalid attr_names:" + attr_names);
576
588
  }
577
589
  return { code: INVALID_PARAMS, err: ["attr_names"] };
578
590
  }
579
591
 
580
592
  const field_names = this.meta.property_fields.map(f => f.name);
593
+ const ref_fields = [];
594
+ const link_fields = [];
581
595
  const attrs = {};
582
596
  attr_names.split(",").forEach(function (attr) {
583
597
  if (field_names.includes(attr)) {
584
598
  attrs[attr] = 1;
599
+ const field = this.meta.fields_map[attr];
600
+ if (field.link) {
601
+ link_fields.push(field);
602
+ attrs[field.link] = 1;
603
+ } else if (field.ref) {
604
+ ref_fields.push(field);
605
+ }
585
606
  }
586
607
  });
587
608
 
@@ -597,10 +618,12 @@ class Entity {
597
618
  }
598
619
  }
599
620
 
600
- const converted = await this.convert_ref_attrs(results);
621
+ const list_link = await this.read_link_attrs(results, link_fields);
622
+ const converted = await this.convert_ref_attrs(list_link, ref_fields);
623
+
601
624
  if (converted && converted.length == 1) {
602
625
  if (is_log_debug()) {
603
- log_debug("read_entity_properties with query:" + JSON.stringify(query) + ",attrs:" + JSON.stringify(attrs) + ",converted:" + JSON.stringify(converted));
626
+ log_debug("read_entity with query:" + JSON.stringify(query) + ",attrs:" + JSON.stringify(attrs) + ",converted:" + JSON.stringify(converted));
604
627
  }
605
628
  return { code: SUCCESS, data: converted[0] };
606
629
  }
@@ -877,17 +900,17 @@ class Entity {
877
900
  * @param {element of object} elements
878
901
  * @returns
879
902
  */
880
- async convert_ref_attrs(elements) {
881
- if (elements && this.meta.ref_fields) {
882
- for (let i = 0; i < this.meta.ref_fields.length; i++) {
883
- const ref_field = this.meta.ref_fields[i];
903
+ async convert_ref_attrs(elements, ref_fields) {
904
+ if (elements && ref_fields && ref_fields.length > 0) {
905
+ for (let i = 0; i < ref_fields.length; i++) {
906
+ const ref_field = ref_fields[i];
884
907
  let id_array = [];
885
908
  for (let j = 0; j < elements.length; j++) {
886
909
  const obj = elements[j];
887
910
  const value = obj[ref_field.name];
888
911
  if (Array.isArray(value)) {
889
912
  id_array = id_array.concat(value);
890
- } else {
913
+ } else if (value) {
891
914
  id_array.push(value);
892
915
  }
893
916
  }
@@ -896,13 +919,16 @@ class Entity {
896
919
  const ref_meta = get_entity_meta(ref_field.ref);
897
920
  const ref_entity = new Entity(ref_meta);
898
921
  const ref_labels = await ref_entity.get_ref_labels(id_array);
899
- const label_map_obj = map_array_to_obj(ref_labels, "_id", ref_meta.ref_label);
922
+ const id_key = "_id";
923
+ const label_map_obj = map_array_to_obj(ref_labels, id_key, ref_meta.ref_label);
900
924
  for (let j = 0; j < elements.length; j++) {
901
925
  const obj = elements[j];
902
926
  const value = obj[ref_field.name];
927
+ obj[ref_field.name + id_key] = value;
928
+
903
929
  if (Array.isArray(value)) {
904
930
  obj[ref_field.name] = value.map(v => label_map_obj[v]);
905
- } else {
931
+ } else if (value) {
906
932
  obj[ref_field.name] = label_map_obj[value];
907
933
  }
908
934
  }
@@ -911,6 +937,84 @@ class Entity {
911
937
  return elements;
912
938
  }
913
939
 
940
+ /**
941
+ * read the link attrs
942
+ * @param {*} elements
943
+ * @returns
944
+ */
945
+ async read_link_attrs(elements, link_fields) {
946
+ if (elements && link_fields && link_fields.length > 0) {
947
+ //key is entity name, value is array of fields
948
+ const entity_attr_map = link_fields.reduce((map, field) => {
949
+ const link_field = this.meta.fields_map[field.link];
950
+ if (map[link_field.ref]) {
951
+ map[link_field.ref].push(field.name);
952
+ } else {
953
+ map[link_field.ref] = [field.name];
954
+ }
955
+ return map;
956
+ }, {});
957
+
958
+ //key entity, value: attr property used to query
959
+ const entity_filter_map = link_fields.reduce((map, field) => {
960
+ const link_field = this.meta.fields_map[field.link];
961
+ if (map[link_field.ref]) {
962
+ (!map[link_field.ref].includes(link_field.name)) && map[link_field.ref].push(link_field.name);
963
+ } else {
964
+ map[link_field.ref] = [link_field.name];
965
+ }
966
+ return map;
967
+ }, {});
968
+
969
+ const entities = Object.keys(entity_filter_map);
970
+ for (let i = 0; i < entities.length; i++) {
971
+ const meta = get_entity_meta(entities[i]);
972
+ const entity = new Entity(meta);
973
+ let id_array = [];
974
+ for (let j = 0; j < elements.length; j++) {
975
+ const obj = elements[j];
976
+ const linked_attrs = entity_filter_map[entities[i]];
977
+ for (let k = 0; k < linked_attrs.length; k++) {
978
+ const id = obj[linked_attrs[k]];
979
+ id_array.push(id);
980
+ }
981
+ }
982
+ id_array = unique(id_array);
983
+ const query = oid_queries(id_array);
984
+ const attr_fields = entity_attr_map[entities[i]];
985
+ const attrs = {};
986
+ const ref_fields = [];
987
+ attr_fields.forEach((attr) => {
988
+ attrs[attr] = 1;
989
+ const field = meta.fields_map[attr];
990
+ if (!field.link && field.ref) {
991
+ ref_fields.push(field);
992
+ }
993
+ });
994
+
995
+ const ref_entity_items = await entity.find(query, attrs);
996
+ if (ref_entity_items && ref_entity_items.length > 0) {
997
+ await entity.convert_ref_attrs(ref_entity_items, ref_fields);
998
+
999
+ for (let j = 0; j < elements.length; j++) {
1000
+ const obj = elements[j];
1001
+ const linked_attrs = entity_filter_map[entities[i]];
1002
+ for (let k = 0; k < linked_attrs.length; k++) {
1003
+ const id = obj[linked_attrs[k]];
1004
+ const [link_obj] = ref_entity_items.filter(o => o._id + "" == id);
1005
+ if (link_obj) {
1006
+ const copy_obj = { ...link_obj };
1007
+ delete copy_obj["_id"];
1008
+ elements[j] = { ...obj, ...copy_obj };
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+ return elements;
1016
+ }
1017
+
914
1018
  /**
915
1019
  * get ref labels of the object, use ref_filter
916
1020
  * @returns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hola-server",
3
- "version": "0.3.18",
3
+ "version": "0.3.21",
4
4
  "description": "a meta programming framework used to build nodejs restful api",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/router/read.js CHANGED
@@ -59,7 +59,7 @@ const init_read_router = function (router, meta) {
59
59
  res.json({ code: code, err: err, total: total, data: data });
60
60
  }));
61
61
 
62
- router.post('/read', wrap_http(async function (req, res) {
62
+ router.post('/read_entity', wrap_http(async function (req, res) {
63
63
  let params = required_post_params(req, ["_id", "attr_names"]);
64
64
  if (params === null) {
65
65
  res.json({ code: NO_PARAMS, err: '[_id,attr_names] checking params are failed!' });
@@ -74,7 +74,7 @@ const init_read_router = function (router, meta) {
74
74
  res.json({ code: code, err: err, data: data });
75
75
  }));
76
76
 
77
- router.post('/read_properties', wrap_http(async function (req, res) {
77
+ router.post('/read_property', wrap_http(async function (req, res) {
78
78
  let params = required_post_params(req, ["_id", "attr_names"]);
79
79
  if (params === null) {
80
80
  res.json({ code: NO_PARAMS, err: '[_id,attr_names] checking params are failed!' });
@@ -82,7 +82,7 @@ const init_read_router = function (router, meta) {
82
82
  }
83
83
 
84
84
  const { _id, attr_names } = params;
85
- const { code, err, data } = await entity.read_entity_properties(_id, attr_names);
85
+ const { code, err, data } = await entity.read_property(_id, attr_names);
86
86
  if (!has_value(code)) {
87
87
  throw new Error("the method should return code");
88
88
  }
package/test/core/meta.js CHANGED
@@ -274,6 +274,68 @@ describe('EntityMeta', function () {
274
274
  });
275
275
  });
276
276
 
277
+ it('should fails for wrong link ', function () {
278
+ throws(() => {
279
+ const entity_meta1 = new EntityMeta({
280
+ collection: "user_link1",
281
+ primary_keys: ["name"],
282
+ fields: [
283
+ { name: "name", type: "string", required: true },
284
+ { name: "role", type: "string", link: "other", required: true },
285
+ ]
286
+ });
287
+ entity_meta1.validate_meta_info();
288
+ });
289
+ throws(() => {
290
+ const entity_meta1 = new EntityMeta({
291
+ collection: "user_link2",
292
+ primary_keys: ["name"],
293
+ fields: [
294
+ { name: "name", type: "string", required: true },
295
+ { name: "role", type: "string", link: "other" },
296
+ ]
297
+ });
298
+ entity_meta1.validate_meta_info();
299
+ });
300
+
301
+ throws(() => {
302
+ const entity_meta1 = new EntityMeta({
303
+ collection: "user_link3",
304
+ primary_keys: ["name"],
305
+ fields: [
306
+ { name: "name", type: "string", required: true },
307
+ { name: "role", link: "other" },
308
+ ]
309
+ });
310
+ entity_meta1.validate_meta_info();
311
+ });
312
+ });
313
+
314
+ it('should success for link ', function () {
315
+ const entity_meta1 = new EntityMeta({
316
+ collection: "role_link_other",
317
+ primary_keys: ["name"],
318
+ ref_label: "name",
319
+ fields: [
320
+ { name: "name", type: "string", required: true },
321
+ { name: "desc", type: "string", required: true },
322
+ ]
323
+ });
324
+
325
+ const entity_meta2 = new EntityMeta({
326
+ collection: "user_link_other",
327
+ primary_keys: ["name"],
328
+ fields: [
329
+ { name: "name", type: "string", required: true },
330
+ { name: "user_role", type: "string", ref: "role_link_other", required: true },
331
+ { name: "desc", link: "user_role", list: true },
332
+ ]
333
+ });
334
+
335
+ strictEqual(entity_meta1.validate_meta_info(), true);
336
+ strictEqual(entity_meta2.validate_meta_info(), true);
337
+ });
338
+
277
339
  it('should fails for wrong primary key type', function () {
278
340
  throws(() => {
279
341
  const entity_meta1 = new EntityMeta({
@@ -1,4 +1,4 @@
1
- const { SUCCESS, ERROR, NO_PARAMS, INVALID_PARAMS, DUPLICATE_KEY, REF_NOT_FOUND } = require('../../http/code');
1
+ const { SUCCESS, } = require('../../http/code');
2
2
  const { strictEqual, deepStrictEqual } = require('assert');
3
3
  const { Entity } = require('../../db/entity');
4
4
  const { EntityMeta } = require('../../core/meta');
@@ -18,7 +18,8 @@ const user = {
18
18
  { name: "role", type: "array", ref: "role_read", required: true },
19
19
  { name: "depart", type: "string", ref: "department_read", required: true },
20
20
  { name: "status", type: "boolean" },
21
- { name: "desc", type: "string", search: false }
21
+ { name: "desc", type: "string", search: false },
22
+ { name: "people", link: "depart" },
22
23
  ]
23
24
  };
24
25
 
@@ -32,7 +33,8 @@ const department = {
32
33
  ref_label: "name",
33
34
  fields: [
34
35
  { name: "name", required: true },
35
- { name: "desc", type: "string" }
36
+ { name: "people", type: "int" },
37
+ { name: "desc", type: "string" },
36
38
  ]
37
39
  };
38
40
 
@@ -65,6 +67,7 @@ const department_entity = new Entity(department_meta);
65
67
  const init_db = async () => {
66
68
  await user_entity.delete({});
67
69
  await role_entity.delete({});
70
+ await department_entity.delete({});
68
71
 
69
72
  await role_entity.create_entity({ name: "admin", status: true });
70
73
  await role_entity.create_entity({ name: "user", status: true });
@@ -72,8 +75,10 @@ const init_db = async () => {
72
75
  await role_entity.create_entity({ name: "user2", status: true });
73
76
  await role_entity.create_entity({ name: "user3", status: true });
74
77
 
75
- await department_entity.create_entity({ name: "dev" });
76
- await department_entity.create_entity({ name: "test" });
78
+ await department_entity.create_entity({ name: "dev", people: 20 });
79
+ const result = await department_entity.create_entity({ name: "test", people: 30 });
80
+ strictEqual(result.err, undefined);
81
+ strictEqual(result.code, SUCCESS);
77
82
 
78
83
  const { code, err } = await user_entity.create_entity({ "name": "user1", pwd: "pwd", age: "10", depart: "dev", role: "user", status: "true", email: "test@test.com", desc: "abcd" });
79
84
  strictEqual(err, undefined);
@@ -100,7 +105,7 @@ describe('Entity Query', function () {
100
105
  it('search user by name', async function () {
101
106
  await init_db();
102
107
 
103
- const query = { "attr_names": "name,age", page: "1", limit: "10", sort_by: "name", desc: "true" };
108
+ const query = { "attr_names": "name,age,depart,people", page: "1", limit: "10", sort_by: "name", desc: "true" };
104
109
  const params = { age: "20" };
105
110
 
106
111
  const { code, err, total, data } = await user_entity.list_entity(query, null, params);
@@ -110,6 +115,8 @@ describe('Entity Query', function () {
110
115
  strictEqual(data.length, 3);
111
116
  strictEqual(data[0].age, 20);
112
117
  strictEqual(data[0].name, "user15");
118
+ strictEqual(data[0].people, 30);
119
+ strictEqual(data[0].depart, "test");
113
120
  strictEqual(data[0].status, undefined);
114
121
  });
115
122