@steedos/service-rest 2.6.10-beta.9 → 2.7.0-beta.10

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": "@steedos/service-rest",
3
- "version": "2.6.10-beta.9",
3
+ "version": "2.7.0-beta.10",
4
4
  "main": "package.service.js",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -16,9 +16,9 @@
16
16
  "repository": {},
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
- "@steedos/objectql": "2.6.10-beta.9",
20
- "@steedos/service-object-mixin": "2.6.10-beta.9",
19
+ "@steedos/objectql": "2.7.0-beta.10",
20
+ "@steedos/service-object-mixin": "2.7.0-beta.10",
21
21
  "lodash": "^4.17.21"
22
22
  },
23
- "gitHead": "63d67e404a721f1947eceed105a56aac8c8d93ec"
23
+ "gitHead": "78659952139c455e4fdd3d581c0d242219e033d0"
24
24
  }
@@ -1,14 +1,15 @@
1
1
  /*
2
2
  * @Author: sunhaolin@hotoa.com
3
3
  * @Date: 2023-03-23 15:12:14
4
- * @LastEditors: 孙浩林 sunhaolin@steedos.com
5
- * @LastEditTime: 2023-09-23 11:50:21
4
+ * @LastEditors: 殷亮辉 yinlianghui@hotoa.com
5
+ * @LastEditTime: 2024-03-14 10:39:22
6
6
  * @Description:
7
7
  */
8
8
  "use strict";
9
9
  // @ts-check
10
10
  const serviceObjectMixin = require('@steedos/service-object-mixin');
11
11
  const { QUERY_DOCS_TOP, REQUEST_SUCCESS_STATUS } = require('./consts')
12
+ const { translateRecords } = require('./translate');
12
13
  const _ = require('lodash')
13
14
  const { getObject } = require('@steedos/objectql');
14
15
 
@@ -53,6 +54,8 @@ module.exports = {
53
54
  * @apiGroup @steedos/service-rest
54
55
  * @apiParam {String} objectName 对象API Name,如:contracts
55
56
  * @apiQuery {String} [fields] 字段名,如:'["name", "description"]'
57
+ * @apiQuery {String} [uiFields] 字段名,如:'["owner", "date"]',此参数中的字段要求在参数fields中存在
58
+ * @apiQuery {String} [expandFields] 字段名,如:'{owner: {fields: ["name"], uiFields: ["owner"], expandFields: ...}}'
56
59
  * @apiQuery {String} [filters] 过滤条件,如:'[["name", "=", "test"],["amount", ">", 100]]'
57
60
  * @apiQuery {String} [top] 获取条数,如:'10',最多5000
58
61
  * @apiQuery {String} [skip] 跳过条数,如:'10'
@@ -90,22 +93,51 @@ module.exports = {
90
93
  params: {
91
94
  objectName: { type: "string" },
92
95
  fields: { type: 'string', optional: true },
96
+ uiFields: { type: 'string', optional: true },
97
+ expandFields: { type: 'string', optional: true },
93
98
  filters: { type: 'string', optional: true },
94
99
  top: { type: 'string', optional: true, default: QUERY_DOCS_TOP },
95
100
  skip: { type: 'string', optional: true },
96
101
  sort: { type: 'string', optional: true }
97
102
  },
98
103
  async handler(ctx) {
104
+ if (process.env.STEEDOS_DEBUG) {
105
+ console.time('open api find total time');
106
+ }
107
+
108
+ if (process.env.STEEDOS_DEBUG) {
109
+ console.time('open api find before find');
110
+ }
99
111
  const params = ctx.params
100
- const { objectName, fields, filters, top, skip, sort } = params
112
+ const { objectName, filters, top, skip, sort } = params
101
113
  const userSession = ctx.meta.user;
102
114
 
115
+ let fields = [];
116
+ if(params.fields){
117
+ fields = JSON.parse(params.fields)
118
+ }
119
+
120
+ let uiFields = [];
121
+ if(params.uiFields){
122
+ uiFields = JSON.parse(params.uiFields)
123
+ }
124
+
125
+ let expandFields;
126
+ if(params.expandFields){
127
+ expandFields = JSON.parse(params.expandFields)
128
+ }
129
+
103
130
  const query = {}
104
131
  if (filters) {
105
132
  query.filters = JSON.parse(filters)
106
133
  }
107
134
  if (fields) {
108
- query.fields = JSON.parse(fields)
135
+ let queryFields = fields;
136
+ if(expandFields){
137
+ // 跟GraphQL第一层一样,从库里查的字段要补上expandFields中的字段,即uiFields中依赖的字段可以在fields中定义,也可以在expandFields中字义
138
+ queryFields = _.union(queryFields, _.keys(expandFields));
139
+ }
140
+ query.fields = queryFields;
109
141
  }
110
142
  if (top) {
111
143
  query.top = Number(top)
@@ -126,22 +158,128 @@ module.exports = {
126
158
  }
127
159
  }
128
160
 
129
- const records = await this.find(objectName, query, userSession)
161
+ if (process.env.STEEDOS_DEBUG) {
162
+ console.timeEnd('open api find before find');
163
+ }
164
+
130
165
  const countQuery = {
131
166
  filters: query.filters
132
167
  }
133
- const totalCount = await this.count(objectName, countQuery, userSession)
168
+
169
+ if (process.env.STEEDOS_DEBUG) {
170
+ console.time('open api find find record and count');
171
+ }
172
+ const [records, totalCount] = await Promise.all([
173
+ await this.find(objectName, query, userSession),
174
+ await this.count(objectName, countQuery, userSession)
175
+ ])
176
+ if (process.env.STEEDOS_DEBUG) {
177
+ console.timeEnd('open api find find record and count');
178
+ }
179
+
180
+
181
+ if (process.env.STEEDOS_DEBUG) {
182
+ console.time('open api find translateRecords');
183
+ }
184
+ const translatedRecords = await translateRecords(records, objectName, fields, uiFields, expandFields, userSession);
185
+ if (process.env.STEEDOS_DEBUG) {
186
+ console.timeEnd('open api find translateRecords');
187
+ }
188
+
189
+ if (process.env.STEEDOS_DEBUG) {
190
+ console.timeEnd('open api find total time');
191
+ }
134
192
 
135
193
  return {
136
194
  "status": REQUEST_SUCCESS_STATUS,
137
195
  "msg": "",
138
196
  "data": {
139
- "items": records,
197
+ "items": translatedRecords,
140
198
  "total": totalCount
141
199
  }
142
200
  }
143
201
  }
144
202
  },
203
+ /**
204
+ * @api {GET} /api/v1/:objectName/count 获取记录个数
205
+ * @apiVersion 0.0.0
206
+ * @apiName count
207
+ * @apiGroup @steedos/service-rest
208
+ * @apiParam {String} objectName 对象API Name,如:contracts
209
+ * @apiQuery {String} [filters] 过滤条件,如:'[["name", "=", "test"],["amount", ">", 100]]'
210
+ * @apiSuccess {Object[]} count 记录个数
211
+ * @apiSuccessExample {json} Success-Response:
212
+ * HTTP/1.1 200 OK
213
+ * {
214
+ * "status": 0, // 返回 0,表示当前接口正确返回,否则按错误请求处理
215
+ * "msg": "", // 返回接口处理信息
216
+ * "data": {
217
+ * "count": 200
218
+ * }
219
+ * }
220
+ * @apiErrorExample {json} Error-Response:
221
+ * HTTP/1.1 500 Error
222
+ * {
223
+ * "status": -1,
224
+ * "msg": "",
225
+ * "data": {}
226
+ * }
227
+ */
228
+ count: {
229
+ rest: {
230
+ method: "GET",
231
+ path: "/:objectName/count"
232
+ },
233
+ params: {
234
+ objectName: { type: "string" },
235
+ filters: { type: 'string', optional: true },
236
+ },
237
+ async handler(ctx) {
238
+ if (process.env.STEEDOS_DEBUG) {
239
+ console.time('open api count total time');
240
+ }
241
+
242
+ if (process.env.STEEDOS_DEBUG) {
243
+ console.time('open api count before find');
244
+ }
245
+ const params = ctx.params
246
+ const { objectName, filters } = params
247
+ const userSession = ctx.meta.user;
248
+
249
+ const query = {}
250
+ if (filters) {
251
+ query.filters = JSON.parse(filters)
252
+ }
253
+
254
+ if (process.env.STEEDOS_DEBUG) {
255
+ console.timeEnd('open api count before find');
256
+ }
257
+
258
+ const countQuery = {
259
+ filters: query.filters
260
+ }
261
+
262
+ if (process.env.STEEDOS_DEBUG) {
263
+ console.time('open api count find count');
264
+ }
265
+ const count = await this.count(objectName, countQuery, userSession);
266
+ if (process.env.STEEDOS_DEBUG) {
267
+ console.timeEnd('open api count find count');
268
+ }
269
+
270
+ if (process.env.STEEDOS_DEBUG) {
271
+ console.timeEnd('open api count total time');
272
+ }
273
+
274
+ return {
275
+ "status": REQUEST_SUCCESS_STATUS,
276
+ "msg": "",
277
+ "data": {
278
+ "count": count
279
+ }
280
+ }
281
+ }
282
+ },
145
283
  /**
146
284
  * @api {POST} /api/v1/:objectName/search 查询列表记录
147
285
  * @apiVersion 0.0.0
package/translate.js ADDED
@@ -0,0 +1,459 @@
1
+ /*
2
+ * @Author: baozhoutao@steedos.com
3
+ * @Date: 2024-02-26 13:29:53
4
+ * @LastEditors: 殷亮辉 yinlianghui@hotoa.com
5
+ * @LastEditTime: 2024-03-12 17:32:17
6
+ * @Description:
7
+ */
8
+ const _ = require('lodash')
9
+
10
+ const { getSteedosSchema, getUserLocale, absoluteUrl } = require("@steedos/objectql");
11
+ const { translationObject } = require("@steedos/i18n");
12
+ const { formatBasicFieldValue } = require("./utils");
13
+
14
+ const EXPAND_SUFFIX = '__expand';
15
+ const UI_PREFIX = '_ui';
16
+
17
+ function getTranslatedFieldConfig(translatedObject, name) {
18
+ return translatedObject.fields[name.replace(/__label$/, "")];
19
+ }
20
+
21
+ async function translateRecordToUI(record, objectName, selectorFieldNames, userSession){
22
+
23
+ const lng = getUserLocale(userSession);
24
+ let steedosSchema = getSteedosSchema();
25
+ let object = steedosSchema.getObject(objectName);
26
+ let objConfig = await object.toConfig();
27
+ let fields = objConfig.fields;
28
+ // let _object = clone(objConfig);
29
+ translationObject(lng, objConfig.name, objConfig, true);
30
+
31
+ async function _translateToUI(record, selectorFieldNames, parentRecord) {
32
+ let displayObj = {};
33
+ for (const name of selectorFieldNames) {
34
+ if (Object.prototype.hasOwnProperty.call(fields, name)) {
35
+ const field = fields[name];
36
+ try {
37
+ if (_.has(record, name)) {
38
+ const fType = field.type;
39
+ const dataType = field.data_type;
40
+ if (fType == "select") {
41
+ let label = "";
42
+ let map = {};
43
+ let value = record[name];
44
+ let translatedField = getTranslatedFieldConfig(objConfig, name);
45
+ let translatedFieldOptions =
46
+ translatedField && translatedField.options;
47
+ _.forEach(translatedFieldOptions, function (o) {
48
+ map[o.value] = o.label;
49
+ });
50
+ if (field.multiple) {
51
+ let labels = [];
52
+ _.forEach(value, function (v) {
53
+ labels.push(map[v]);
54
+ });
55
+ label = labels.join(",");
56
+ } else {
57
+ label = map[value];
58
+ }
59
+ displayObj[name] = label;
60
+ } else if (fType == "lookup" && (_.isString(field.reference_to) || (!_.has(field, 'reference_to') && !_.has(field, '_reference_to')))) {
61
+ if (_.isString(field.reference_to)) {
62
+ let refTo = field.reference_to;
63
+
64
+ let refField = field.reference_to_field || '_id';
65
+
66
+ if (refTo === 'users') {
67
+ refTo = 'space_users';
68
+ refField = 'user'
69
+ }
70
+
71
+ let refValue = record[name];
72
+ if (!refValue) {
73
+ continue;
74
+ }
75
+ let refObj = steedosSchema.getObject(refTo);
76
+ let nameFieldKey = await refObj.getNameFieldKey();
77
+ let refFilters = null;
78
+
79
+ if (field.multiple) {
80
+ refFilters = [refField, "in", refValue]
81
+ } else {
82
+ refFilters = [refField, "=", refValue]
83
+ }
84
+
85
+ // 判断如果是 reference_to = object_fields && reference_to_field = name, 则额外添加查询条件 object 查询条件;
86
+ if (refTo === 'object_fields' && refField == 'name') {
87
+ if(objConfig.name === 'objects'){
88
+ refFilters = [['object', '=', parentRecord['name']], refFilters]
89
+ }else{
90
+ const refToObjectsField = _.find(fields, (_field) => {
91
+ return _field.reference_to === 'objects'
92
+ })
93
+ if (refToObjectsField) {
94
+ refFilters = [['object', '=', parentRecord[refToObjectsField.name]], refFilters]
95
+ }
96
+ }
97
+
98
+ }
99
+
100
+ // 判断如果是 reference_to = object_actions && reference_to_field = name, 则额外添加查询条件 object 查询条件;
101
+ if (refTo === 'object_actions' && refField == 'name') {
102
+ const refToObjectsField = _.find(fields, (_field) => {
103
+ return _field.reference_to === 'objects'
104
+ })
105
+ if (refToObjectsField) {
106
+ refFilters = [['object', '=', parentRecord[refToObjectsField.name]], refFilters]
107
+ }
108
+ }
109
+
110
+ if (field.multiple) {
111
+ let refRecords = await refObj.find({
112
+ filters: refFilters,
113
+ fields: [nameFieldKey],
114
+ });
115
+ displayObj[name] = _.map(refRecords, (item) => {
116
+ return {
117
+ objectName: refTo,
118
+ value: item ? item._id : refValue,
119
+ label: item ? item[nameFieldKey] : refValue
120
+ }
121
+ })
122
+ } else {
123
+ let refRecord = (
124
+ await refObj.find({
125
+ filters: refFilters,
126
+ fields: [nameFieldKey],
127
+ })
128
+ )[0];
129
+ if (refRecord) {
130
+ displayObj[name] = {
131
+ objectName: refTo,
132
+ value: refRecord._id,
133
+ label: refRecord[nameFieldKey]
134
+ };
135
+ } else {
136
+ displayObj[name] = {
137
+ objectName: refTo,
138
+ value: refValue,
139
+ label: refValue
140
+ };
141
+ }
142
+ }
143
+ } else {
144
+ let refValue = record[name];
145
+ if (!refValue) {
146
+ continue;
147
+ }
148
+ if (field.multiple && _.isArray(refValue)) {
149
+ _.each(refValue, (item) => {
150
+ displayObj[name] = {
151
+ value: item,
152
+ label: item
153
+ };
154
+ })
155
+ } else {
156
+ displayObj[name] = {
157
+ value: refValue,
158
+ label: refValue
159
+ };
160
+ }
161
+ }
162
+
163
+ } else if (fType == "master_detail" && _.isString(field.reference_to)) {
164
+ let refTo = field.reference_to;
165
+ let refField = field.reference_to_field || '_id';
166
+
167
+ if (refTo === 'users') {
168
+ refTo = 'space_users';
169
+ refField = 'user'
170
+ }
171
+ let refValue = record[name];
172
+ if (!refValue) {
173
+ continue;
174
+ }
175
+ let refObj = steedosSchema.getObject(refTo);
176
+ let nameFieldKey = await refObj.getNameFieldKey();
177
+
178
+ if (field.multiple) {
179
+ let refRecords = await refObj.find({
180
+ filters: [refField, "in", refValue],
181
+ fields: [nameFieldKey],
182
+ });
183
+ displayObj[name] = _.map(refRecords, (item) => {
184
+ return {
185
+ objectName: refTo,
186
+ value: item._id,
187
+ label: item[nameFieldKey]
188
+ }
189
+ })
190
+ } else {
191
+ let refRecord = (
192
+ await refObj.find({
193
+ filters: [refField, "=", refValue],
194
+ fields: [nameFieldKey],
195
+ })
196
+ )[0];
197
+ if (refRecord) {
198
+ displayObj[name] = {
199
+ objectName: refTo,
200
+ value: refRecord._id,
201
+ label: refRecord[nameFieldKey]
202
+ };
203
+ }
204
+ }
205
+ } else if ((fType == "master_detail" || fType == "lookup") && ((field.reference_to && !_.isString(field.reference_to)) || _.isString(field._reference_to))) {
206
+ let refValue = record[name];
207
+ if (!refValue) {
208
+ continue;
209
+ }
210
+ let refTo = refValue.o;
211
+ let refValues = refValue.ids;
212
+ if (!refTo) {
213
+ continue;
214
+ }
215
+ let refObj = steedosSchema.getObject(refTo);
216
+ let nameFieldKey = await refObj.getNameFieldKey();
217
+ let refRecords = await refObj.find({
218
+ filters: [`_id`, "in", refValues],
219
+ fields: [nameFieldKey]
220
+ });
221
+
222
+ displayObj[name] = _.map(refRecords, (item) => {
223
+ return {
224
+ objectName: refTo,
225
+ value: item._id,
226
+ label: item[nameFieldKey]
227
+ }
228
+ })
229
+ } else if (fType == "formula") {
230
+ displayObj[name] = formatBasicFieldValue(field.data_type, field, record[name], objConfig, userSession);
231
+ } else if (fType == "summary") {
232
+ displayObj[name] = formatBasicFieldValue(dataType, field, record[name], objConfig, userSession);
233
+ } else if (fType == "image" || fType == "file" || fType === 'avatar') {
234
+ const optionsStr = fType == "file" ? '?download=1' : ''
235
+ let fileValue = null;
236
+ let value = record[name];
237
+ if (!value) {
238
+ continue;
239
+ }
240
+ // TODO: cfs_images_filerecord对象不存在,需要额外处理
241
+ let storageName = getFileStorageName(fType)
242
+ let fileObjectName = `cfs_${storageName}_filerecord`;
243
+ let fileObject = steedosSchema.getObject(fileObjectName);
244
+ const fileNameFieldKey = "original.name";
245
+ if (field.multiple) {
246
+ let fileRecords = await fileObject.find({
247
+ filters: [`_id`, "in", value],
248
+ fields: ['_id', fileNameFieldKey, 'original.size', 'original.type'],
249
+ });
250
+ fileValue = _.map(fileRecords, (fileRecord) => {
251
+ return {
252
+ name: fileRecord.original?.name,
253
+ url: absoluteUrl(`/api/files/${storageName}/${fileRecord._id}${optionsStr}`),
254
+ size: fileRecord.original?.size,
255
+ type: fileRecord.original?.type,
256
+ };
257
+ });
258
+ } else {
259
+ let fileRecord = (
260
+ await fileObject.find({
261
+ filters: [`_id`, "=", value],
262
+ fields: ['_id', fileNameFieldKey, 'original.size', 'original.type'],
263
+ })
264
+ )[0];
265
+ if (fileRecord) {
266
+ fileValue = {
267
+ name: fileRecord["original"]["name"],
268
+ url: absoluteUrl(`/api/files/${storageName}/${value}${optionsStr}`),
269
+ size: fileRecord.original?.size,
270
+ type: fileRecord.original?.type
271
+ };
272
+ } else {
273
+ fileValue = {
274
+ url: value
275
+ };
276
+ }
277
+ }
278
+ displayObj[name] = fileValue;
279
+ } else if (fType == "filesize") {
280
+ displayObj[name] = formatFileSize(record[name]);
281
+ }
282
+ else if (fType === 'object') {
283
+ if (record[name] && _.isObject(record[name])) {
284
+ const _doc = {}
285
+ _.each(record[name], function (v, k) {
286
+ const newKey = `${name}.${k}`
287
+ _doc[newKey] = v
288
+ })
289
+ const objectFieldDoc = await _translateToUI(_doc, Object.keys(_doc), record)
290
+ const objectDoc = {}
291
+ _.each(objectFieldDoc, function (v, k) {
292
+ const newKey = k.replace(`${name}.`, '')
293
+ objectDoc[newKey] = v
294
+ })
295
+ displayObj[name] = objectDoc
296
+ }
297
+ }
298
+ else if (fType === 'grid' || 'table' === fType) {
299
+ if (record[name] && _.isArray(record[name])) {
300
+ const gridDocs = []
301
+ for (const gridDoc of record[name]) {
302
+ const _doc = {}
303
+ _.each(gridDoc, function (v, k) {
304
+ const newKey = `${name}.$.${k}`
305
+ _doc[newKey] = v
306
+ })
307
+ const objectFieldDoc = await _translateToUI(_doc, Object.keys(_doc), record)
308
+ const objectDoc = {}
309
+ _.each(objectFieldDoc, function (v, k) {
310
+ const newKey = k.replace(`${name}.$.`, '')
311
+ objectDoc[newKey] = v
312
+ })
313
+ gridDocs.push(objectDoc)
314
+ }
315
+
316
+ displayObj[name] = gridDocs
317
+ }
318
+ }
319
+ else {
320
+ displayObj[name] = formatBasicFieldValue(fType, field, record[name], objConfig, userSession);
321
+ }
322
+ }
323
+ else {
324
+ displayObj[name] = ""; // 如果值为空,均返回空字符串
325
+ }
326
+ } catch (error) {
327
+ displayObj[name] = record[name];
328
+ // console.warn(error)
329
+ }
330
+ }
331
+ }
332
+ return displayObj
333
+ }
334
+
335
+ let uiDoc = await _translateToUI(record, selectorFieldNames, record)
336
+ return uiDoc;
337
+ }
338
+
339
+ async function translateRecordToExpand(record, objectName, expandFields, userSession) {
340
+ let steedosSchema = getSteedosSchema();
341
+ let object = steedosSchema.getObject(objectName);
342
+ let objConfig = await object.toConfig();
343
+ let fields = objConfig.fields;
344
+
345
+ async function _translateToExpand(record, expandFields) {
346
+ let expandObj = {};
347
+ for (const fieldName in expandFields) {
348
+ const expandField = expandFields[fieldName];
349
+ let expandFieldFields = expandField.fields || [];
350
+ const expandFieldUiFields = expandField.uiFields;
351
+ const expandFieldExpandFields = expandField.expandFields;
352
+ if(expandFieldFields.length === 0 && _.isEmpty(expandFieldExpandFields)){
353
+ continue;
354
+ }
355
+
356
+ if (Object.prototype.hasOwnProperty.call(fields, fieldName) && expandFieldFields.length > 0) {
357
+ const field = fields[fieldName];
358
+ try {
359
+ if (field && _.has(record, fieldName)) {
360
+ let isLookup = (field.type == "lookup" || field.type == "master_detail") &&
361
+ field.reference_to && _.isString(field.reference_to);
362
+ let isFile = field.type == "image" || field.type == "file";
363
+ if (isLookup || isFile){
364
+ let refTo = field.reference_to;
365
+ let refField = field.reference_to_field || '_id';
366
+ if (isFile) {
367
+ // TODO: cfs_images_filerecord对象不存在,需要额外处理
368
+ refTo = field.type == "image" ? "cfs_images_filerecord" : "cfs_files_filerecord";
369
+ }
370
+
371
+ let refValue = record[fieldName];
372
+ if (!refValue) {
373
+ // 如果值为空,跟GraphQL一样,返回null值
374
+ expandObj[fieldName + EXPAND_SUFFIX] = null;
375
+ continue;
376
+ }
377
+ let refObj = steedosSchema.getObject(refTo);
378
+ let refFilters = null;
379
+
380
+ if (field.multiple) {
381
+ refFilters = [refField, "in", refValue]
382
+ } else {
383
+ refFilters = [refField, "=", refValue]
384
+ }
385
+
386
+ let queryFields = expandFieldFields;
387
+ if(expandFieldExpandFields){
388
+ // 跟GraphQL第一层一样,从库里查的字段要补上expandFields中的字段,即uiFields中依赖的字段可以在fields中定义,也可以在expandFields中字义
389
+ queryFields = _.union(queryFields, _.keys(expandFieldExpandFields));
390
+ }
391
+
392
+ const queryFilters = [refFilters];
393
+ const spaceId = userSession.spaceId;
394
+ if (refField && refField != '_id' && refTo != 'users' && refTo != 'spaces' && spaceId) {
395
+ queryFilters.push(["space", "=", spaceId])
396
+ }
397
+
398
+ let refRecords = await refObj.find({
399
+ filters: queryFilters,
400
+ fields: queryFields,
401
+ });
402
+ let translatedRecords = await translateRecords(refRecords, refTo, expandFieldFields, expandFieldUiFields, expandFieldExpandFields, userSession);
403
+ if(field.multiple){
404
+ expandObj[fieldName + EXPAND_SUFFIX] = translatedRecords;
405
+ }
406
+ else{
407
+ expandObj[fieldName + EXPAND_SUFFIX] = translatedRecords[0];
408
+ }
409
+ }
410
+ }
411
+ else {
412
+ // 如果值为空,跟GraphQL一样,返回null值
413
+ expandObj[fieldName + EXPAND_SUFFIX] = null;
414
+ }
415
+ } catch (error) {
416
+ // 如果报错,跟GraphQL一样,返回null值
417
+ expandObj[fieldName + EXPAND_SUFFIX] = null;
418
+ }
419
+
420
+ }
421
+ };
422
+ return expandObj
423
+ }
424
+
425
+ let expandProps = await _translateToExpand(record, expandFields)
426
+ return expandProps;
427
+ }
428
+
429
+ async function translateRecords(records, objectName, fields, uiFields, expandFields, userSession) {
430
+ var hasUiFields = !_.isEmpty(uiFields);
431
+ var hasExpandFields = !_.isEmpty(expandFields);
432
+ var emptyRecord = {};
433
+ _.each(fields || [], (field) => {
434
+ // GraphQL那边空值规则是返回null,openApi跟它一样
435
+ emptyRecord[field] = null;
436
+ });
437
+ // 跟GraphQl一样,找出在expandFields中字义过,但是没在fields中定义的字段,需要排除掉这些字段,即只返回fields中指定的字段,record中因为expandFields额外多查出的字段不返回
438
+ let omitFields = _.difference(_.keys(expandFields), fields);
439
+ let resRecords = [];
440
+ for (let record of records) {
441
+ if (hasUiFields || hasExpandFields) {
442
+ record = Object.assign({}, emptyRecord, record); // 跟GraphQl一样,只返回fields中指定的字段,record中多查出的字段不返回
443
+ if (hasUiFields) {
444
+ record[UI_PREFIX] = await translateRecordToUI(record, objectName, uiFields, userSession);
445
+ }
446
+ if (hasExpandFields) {
447
+ let expandProps = await translateRecordToExpand(record, objectName, expandFields, userSession);
448
+ Object.assign(record, expandProps);
449
+ }
450
+ }
451
+ record = _.omit(record, omitFields);
452
+ resRecords.push(record);
453
+ }
454
+ return resRecords;
455
+ }
456
+
457
+ module.exports = {
458
+ translateRecords
459
+ }
package/utils.js ADDED
@@ -0,0 +1,150 @@
1
+ /*
2
+ * @Author: sunhaolin@hotoa.com
3
+ * @Date: 2023-02-06 16:44:55
4
+ * @LastEditors: 殷亮辉 yinlianghui@hotoa.com
5
+ * @LastEditTime: 2024-03-12 16:52:36
6
+ * @Description:
7
+ */
8
+
9
+ const moment = require('moment');
10
+ const _ = require("underscore");
11
+ const { getSteedosSchema } = require('@steedos/objectql');
12
+
13
+ function formatFileSize(fileSize) {
14
+ var rev, unit;
15
+ rev = fileSize / 1024.00;
16
+ unit = 'KB';
17
+ if (rev > 1024.00) {
18
+ rev = rev / 1024.00;
19
+ unit = 'MB';
20
+ }
21
+ if (rev > 1024.00) {
22
+ rev = rev / 1024.00;
23
+ unit = 'GB';
24
+ }
25
+ return rev.toFixed(2) + unit;
26
+ };
27
+
28
+ function getFileStorageName(type) {
29
+ switch (type) {
30
+ case 'avatar':
31
+ return 'avatars'
32
+ case 'image':
33
+ return 'images'
34
+ case 'file':
35
+ return 'files'
36
+ default:
37
+ break;
38
+ }
39
+ };
40
+
41
+ function formatBasicFieldValue(valueType, field, value, objectConfig, userSession) {
42
+ switch (valueType) {
43
+ case 'text':
44
+ case 'textarea':
45
+ case 'html_text':
46
+ case 'color':
47
+ case 'autonumber':
48
+ case 'url':
49
+ case 'email':
50
+ case 'html':
51
+ case 'markdown':
52
+ case 'code':
53
+ return value || "";
54
+ case 'boolean':
55
+ return value ? "√" : ""
56
+ case 'date':
57
+ return value ? moment.utc(value).format("YYYY-MM-DD") : '';
58
+ case 'datetime':
59
+ return value ? moment(value).utcOffset(userSession.utcOffset || 8).format("YYYY-MM-DD HH:mm") : '';
60
+ case 'time':
61
+ return value ? moment.utc(value).format("HH:mm") : '';
62
+ case 'number':
63
+ case 'currency':
64
+ return numberToString(value, field.scale, field.enable_thousands === false);
65
+ case 'percent':
66
+ const str = numberToString(value * 100, field.scale, field.enable_thousands === false);
67
+ return str ? `${str}%` : ''
68
+ case 'password':
69
+ return _.isString(value) ? "******" : ""
70
+ default:
71
+ // console.log(field)
72
+ console.error(
73
+ `Graphql Display: need to handle new field type ${field.type} for ${objectConfig.name}.`
74
+ );
75
+ return value || "";
76
+ }
77
+ }
78
+
79
+ function numberToString(number, scale, notThousands = false) {
80
+ if (typeof number === "number") {
81
+ number = number.toString();
82
+ }
83
+ if (!number) {
84
+ return '';
85
+ }
86
+ if (number !== "NaN") {
87
+ if (scale || scale === 0) {
88
+ number = Number(number).toFixed(scale);
89
+ }
90
+ if (!notThousands) {
91
+ if (!(scale || scale === 0)) {
92
+ // 没定义scale时,根据小数点位置算出scale值
93
+ let regDots = number.match(/\.(\d+)/);
94
+ scale = regDots && regDots[1] && regDots[1].length
95
+ if (!scale) {
96
+ scale = 0;
97
+ }
98
+ }
99
+ let reg = /(\d)(?=(\d{3})+\.)/g;
100
+ if (scale === 0) {
101
+ reg = /(\d)(?=(\d{3})+\b)/g;
102
+ }
103
+ number = number.replace(reg, '$1,');
104
+ }
105
+ return number;
106
+ } else {
107
+ return "";
108
+ }
109
+ }
110
+
111
+ async function callObjectServiceAction(actionName, userSession, data) {
112
+ const broker = getSteedosSchema().broker;
113
+ return broker.call(actionName, data, { meta: { user: userSession } })
114
+ }
115
+
116
+ // 获取object元数据
117
+ function getLocalService(objectApiName) {
118
+ let steedosSchema = getSteedosSchema();
119
+ return steedosSchema.broker.getLocalService(getObjectServiceName(objectApiName));
120
+ }
121
+
122
+ function correctName(name) {
123
+ return name.replace(/\./g, "_").replace(/\$/g, "_");
124
+ }
125
+
126
+ function _getRelatedType(relatedFieldName, relatedObjName) {
127
+ return `${relatedFieldName}(fields: [String], filters: JSON, top: Int, skip: Int, sort: String): [${relatedObjName}] `;
128
+ }
129
+
130
+ function getObjectServiceName(objectApiName){
131
+ return `@${objectApiName}`;
132
+ }
133
+
134
+ function getGraphqlServiceName(){
135
+ return 'graphql';
136
+ }
137
+
138
+
139
+ module.exports = {
140
+ formatFileSize,
141
+ getFileStorageName,
142
+ formatBasicFieldValue,
143
+ numberToString,
144
+ callObjectServiceAction,
145
+ getLocalService,
146
+ correctName,
147
+ _getRelatedType,
148
+ getObjectServiceName,
149
+ getGraphqlServiceName
150
+ }