@things-factory/dataset 5.0.0-alpha.15 → 5.0.0-alpha.17
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/client/pages/data-ooc.js +35 -3
- package/dist-server/controllers/create-data-sample.js +102 -0
- package/dist-server/controllers/create-data-sample.js.map +1 -0
- package/dist-server/routes.js +5 -16
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/data-ooc/data-ooc-subscription.js +65 -0
- package/dist-server/service/data-ooc/data-ooc-subscription.js.map +1 -0
- package/dist-server/service/data-ooc/index.js +2 -1
- package/dist-server/service/data-ooc/index.js.map +1 -1
- package/dist-server/service/data-sample/data-sample-mutation.js +2 -80
- package/dist-server/service/data-sample/data-sample-mutation.js.map +1 -1
- package/package.json +14 -14
- package/server/controllers/create-data-sample.ts +139 -0
- package/server/routes.ts +12 -23
- package/server/service/data-ooc/data-ooc-subscription.ts +51 -0
- package/server/service/data-ooc/index.ts +2 -1
- package/server/service/data-sample/data-sample-mutation.ts +2 -105
- package/translations/ms.json +2 -2
- package/translations/zh.json +2 -2
package/client/pages/data-ooc.js
CHANGED
@@ -56,9 +56,9 @@ export class DataOoc extends connect(store)(localize(i18next)(PageView)) {
|
|
56
56
|
help: 'integration/ui/data-ooc',
|
57
57
|
actions: [
|
58
58
|
{
|
59
|
-
title: i18next.t('button.
|
60
|
-
action: this.
|
61
|
-
...CommonButtonStyles.
|
59
|
+
title: i18next.t('button.save'),
|
60
|
+
action: this._updateDataOoc.bind(this),
|
61
|
+
...CommonButtonStyles.save
|
62
62
|
}
|
63
63
|
],
|
64
64
|
exportable: {
|
@@ -350,6 +350,38 @@ export class DataOoc extends connect(store)(localize(i18next)(PageView)) {
|
|
350
350
|
}
|
351
351
|
}
|
352
352
|
|
353
|
+
async _updateDataOoc() {
|
354
|
+
let patches = this.grist.dirtyRecords
|
355
|
+
if (patches && patches.length) {
|
356
|
+
patches = patches.map(patch => {
|
357
|
+
let patchField = patch.id ? { id: patch.id } : {}
|
358
|
+
const dirtyFields = patch.__dirtyfields__
|
359
|
+
for (let key in dirtyFields) {
|
360
|
+
patchField[key] = dirtyFields[key].after
|
361
|
+
}
|
362
|
+
this._setDefaultFieldsValue(patchField)
|
363
|
+
patchField.cuFlag = patch.__dirty__
|
364
|
+
|
365
|
+
return patchField
|
366
|
+
})
|
367
|
+
|
368
|
+
const response = await client.mutate({
|
369
|
+
mutation: gql`
|
370
|
+
mutation ($patches: [DataOocPatch!]!) {
|
371
|
+
updateMultipleDataOoc(patches: $patches) {
|
372
|
+
name
|
373
|
+
}
|
374
|
+
}
|
375
|
+
`,
|
376
|
+
variables: {
|
377
|
+
patches
|
378
|
+
}
|
379
|
+
})
|
380
|
+
|
381
|
+
if (!response.errors) this.grist.fetch()
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
353
385
|
async _deleteDataOoc() {
|
354
386
|
if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
|
355
387
|
const ids = this.grist.selected.map(record => record.id)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.createDataSample = void 0;
|
7
|
+
const data_ooc_1 = require("../service/data-ooc/data-ooc");
|
8
|
+
const shell_1 = require("@things-factory/shell");
|
9
|
+
const data_item_1 = require("../service/data-item/data-item");
|
10
|
+
const data_sample_1 = require("../service/data-sample/data-sample");
|
11
|
+
const data_set_1 = require("../service/data-set/data-set");
|
12
|
+
const data_use_case_1 = require("./data-use-case");
|
13
|
+
const moment_1 = __importDefault(require("moment"));
|
14
|
+
const debug = require('debug')('things-factory:dataset:controller/save-data-sample');
|
15
|
+
// parse variable javascript string pattern
|
16
|
+
const replaceVariables = (keys, dic) => {
|
17
|
+
for (const k in keys) {
|
18
|
+
const matches = keys[k].match(/\$\{\w*\}/g);
|
19
|
+
matches &&
|
20
|
+
matches.forEach(m => {
|
21
|
+
keys[k] = keys[k].replace(m, dic[m.slice(2, -1)]);
|
22
|
+
});
|
23
|
+
}
|
24
|
+
return keys;
|
25
|
+
};
|
26
|
+
// It is required UTC date for Partitioning File System like AWS S3 from Athena.
|
27
|
+
// ex) %YYYY, %MM, %DD
|
28
|
+
const formatDate = (keys, _moment) => {
|
29
|
+
for (const k in keys) {
|
30
|
+
const matches = keys[k].match(/%\w*/g);
|
31
|
+
matches &&
|
32
|
+
matches.forEach(m => {
|
33
|
+
keys[k] = keys[k].replace(m, _moment.format(m.substr(1)));
|
34
|
+
});
|
35
|
+
}
|
36
|
+
return keys;
|
37
|
+
};
|
38
|
+
async function createDataSample(dataSample, context) {
|
39
|
+
const { domain, user, tx } = context.state;
|
40
|
+
const dataSet = await tx.getRepository(data_set_1.DataSet).findOne({
|
41
|
+
where: { id: dataSample.dataSet.id }
|
42
|
+
});
|
43
|
+
const dataItems = await tx.getRepository(data_item_1.DataItem).find({
|
44
|
+
where: {
|
45
|
+
domain,
|
46
|
+
dataSet
|
47
|
+
},
|
48
|
+
order: {
|
49
|
+
sequence: 'DESC'
|
50
|
+
}
|
51
|
+
});
|
52
|
+
const spec = dataItems.reduce((spec, dataItem) => {
|
53
|
+
spec[dataItem.tag] = Object.assign(Object.assign({}, dataItem.spec), { name: dataItem.name /* do we need ? */ });
|
54
|
+
return spec;
|
55
|
+
}, {});
|
56
|
+
var partitionKeys = Object.assign({}, dataSet.partitionKeys);
|
57
|
+
const collectedAt = dataSample.collectedAt || new Date();
|
58
|
+
partitionKeys = formatDate(partitionKeys, (0, moment_1.default)(collectedAt).utc());
|
59
|
+
partitionKeys = replaceVariables(partitionKeys, Object.assign({ domain: domain.subdomain, dataSetId: dataSample.dataSet.id }, dataSample.data));
|
60
|
+
const { ooc, oos } = data_use_case_1.DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {};
|
61
|
+
const result = await tx.getRepository(data_sample_1.DataSample).save(Object.assign(Object.assign({ name: dataSet.name, description: dataSet.description }, dataSample), { domain,
|
62
|
+
partitionKeys,
|
63
|
+
spec,
|
64
|
+
ooc,
|
65
|
+
oos,
|
66
|
+
collectedAt, creator: user, updater: user }));
|
67
|
+
if (ooc || oos) {
|
68
|
+
const dataOoc = await tx.getRepository(data_ooc_1.DataOoc).save({
|
69
|
+
name: dataSet.name,
|
70
|
+
description: dataSet.description,
|
71
|
+
dataSet,
|
72
|
+
dataSample: result,
|
73
|
+
data: dataSample.data,
|
74
|
+
domain,
|
75
|
+
partitionKeys,
|
76
|
+
spec,
|
77
|
+
ooc,
|
78
|
+
oos,
|
79
|
+
state: data_ooc_1.DataOocStatus.CREATED,
|
80
|
+
collectedAt,
|
81
|
+
creator: user,
|
82
|
+
updater: user
|
83
|
+
});
|
84
|
+
shell_1.pubsub.publish('data-ooc', {
|
85
|
+
dataOoc,
|
86
|
+
supervisoryRoleId: dataSet.supervisoryRoleId
|
87
|
+
});
|
88
|
+
shell_1.pubsub.publish('notification', {
|
89
|
+
notification: {
|
90
|
+
domain,
|
91
|
+
type: 'error',
|
92
|
+
title: `Data OOC occurred on '${dataSet.name}'`,
|
93
|
+
body: `Data OOC occurred on '${dataSet.name}'`,
|
94
|
+
url: (0, shell_1.getRedirectSubdomainPath)(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
|
95
|
+
timestamp: collectedAt
|
96
|
+
}
|
97
|
+
});
|
98
|
+
}
|
99
|
+
return result;
|
100
|
+
}
|
101
|
+
exports.createDataSample = createDataSample;
|
102
|
+
//# sourceMappingURL=create-data-sample.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"create-data-sample.js","sourceRoot":"","sources":["../../server/controllers/create-data-sample.ts"],"names":[],"mappings":"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"}
|
package/dist-server/routes.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
const typeorm_1 = require("typeorm");
|
4
|
-
const service_1 = require("./service");
|
5
3
|
const data_item_1 = require("./service/data-item/data-item");
|
6
4
|
const data_sensor_1 = require("./service/data-sensor/data-sensor");
|
5
|
+
const create_data_sample_1 = require("./controllers/create-data-sample");
|
6
|
+
const typeorm_1 = require("typeorm");
|
7
7
|
const debug = require('debug')('things-factory:dataset:routes');
|
8
8
|
process.on('bootstrap-module-global-public-route', (app, globalPublicRouter) => {
|
9
9
|
/*
|
@@ -49,24 +49,13 @@ process.on('bootstrap-module-global-public-route', (app, globalPublicRouter) =>
|
|
49
49
|
sequence: 'DESC'
|
50
50
|
}
|
51
51
|
});
|
52
|
-
|
53
|
-
dataItems.forEach(dataItem => {
|
54
|
-
spec[dataItem.name] = dataItem.spec;
|
55
|
-
});
|
56
|
-
await tx.getRepository(service_1.DataSample).save({
|
57
|
-
domain,
|
58
|
-
name: dataSet.name,
|
59
|
-
description: dataSet.description,
|
60
|
-
partitionKeys: dataSet.partitionKeys,
|
52
|
+
return await (0, create_data_sample_1.createDataSample)({
|
61
53
|
dataSet,
|
62
54
|
data,
|
63
55
|
rawData,
|
64
|
-
spec,
|
65
56
|
source: deviceId,
|
66
|
-
collectedAt: new Date(timestamp)
|
67
|
-
|
68
|
-
updater: user
|
69
|
-
});
|
57
|
+
collectedAt: new Date(timestamp)
|
58
|
+
}, { state: { domain, user, tx } });
|
70
59
|
});
|
71
60
|
return 'OK';
|
72
61
|
});
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":";;AAAA,
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
7
|
+
};
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
10
|
+
};
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
13
|
+
};
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
15
|
+
exports.DataOocSubscription = void 0;
|
16
|
+
const type_graphql_1 = require("type-graphql");
|
17
|
+
const graphql_subscriptions_1 = require("graphql-subscriptions");
|
18
|
+
const shell_1 = require("@things-factory/shell");
|
19
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
20
|
+
const data_ooc_1 = require("./data-ooc");
|
21
|
+
const typeorm_1 = require("typeorm");
|
22
|
+
const debug = require('debug')('things-factory:dataset:data-ooc-subscription');
|
23
|
+
let DataOocSubscription = class DataOocSubscription {
|
24
|
+
dataOoc(payload) {
|
25
|
+
return payload.dataOoc;
|
26
|
+
}
|
27
|
+
};
|
28
|
+
__decorate([
|
29
|
+
(0, type_graphql_1.Subscription)({
|
30
|
+
subscribe: (_, args, context, info) => {
|
31
|
+
var _a;
|
32
|
+
const { domain, user } = context.state;
|
33
|
+
const subdomain = domain === null || domain === void 0 ? void 0 : domain.subdomain;
|
34
|
+
debug('subscribe', subdomain);
|
35
|
+
if (!domain) {
|
36
|
+
throw new Error('domain required');
|
37
|
+
}
|
38
|
+
if (!((_a = user.domains) === null || _a === void 0 ? void 0 : _a.find(d => d.subdomain === subdomain))) {
|
39
|
+
throw new Error(`domain(${subdomain}) is not working for user(${user.email}).`);
|
40
|
+
}
|
41
|
+
return (0, graphql_subscriptions_1.withFilter)(() => shell_1.pubsub.asyncIterator('data-ooc'), async (payload, variables, context, info) => {
|
42
|
+
const { dataOoc, supervisoryRoleId } = payload;
|
43
|
+
const { domain } = dataOoc;
|
44
|
+
if (subdomain !== (domain === null || domain === void 0 ? void 0 : domain.subdomain)) {
|
45
|
+
return false;
|
46
|
+
}
|
47
|
+
// check if the user has that role
|
48
|
+
const userWithRoles = await (0, typeorm_1.getRepository)(auth_base_1.User).findOne({
|
49
|
+
where: { id: user.id },
|
50
|
+
relations: ['roles']
|
51
|
+
});
|
52
|
+
return userWithRoles.roles.find(role => role.id === supervisoryRoleId);
|
53
|
+
})(_, args, context, info);
|
54
|
+
}
|
55
|
+
}),
|
56
|
+
__param(0, (0, type_graphql_1.Root)()),
|
57
|
+
__metadata("design:type", Function),
|
58
|
+
__metadata("design:paramtypes", [Object]),
|
59
|
+
__metadata("design:returntype", data_ooc_1.DataOoc)
|
60
|
+
], DataOocSubscription.prototype, "dataOoc", null);
|
61
|
+
DataOocSubscription = __decorate([
|
62
|
+
(0, type_graphql_1.Resolver)(data_ooc_1.DataOoc)
|
63
|
+
], DataOocSubscription);
|
64
|
+
exports.DataOocSubscription = DataOocSubscription;
|
65
|
+
//# sourceMappingURL=data-ooc-subscription.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"data-ooc-subscription.js","sourceRoot":"","sources":["../../../server/service/data-ooc/data-ooc-subscription.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,+CAA2D;AAC3D,iEAAkD;AAClD,iDAA8C;AAC9C,yDAAgD;AAChD,yCAAoC;AACpC,qCAAuC;AAEvC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,8CAA8C,CAAC,CAAA;AAG9E,IAAa,mBAAmB,GAAhC,MAAa,mBAAmB;IAqC9B,OAAO,CAAS,OAAwD;QACtE,OAAO,OAAO,CAAC,OAAO,CAAA;IACxB,CAAC;CACF,CAAA;AAHC;IApCC,IAAA,2BAAY,EAAC;QACZ,SAAS,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;;YACpC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;YACtC,MAAM,SAAS,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,SAAS,CAAA;YAEnC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;YAE7B,IAAI,CAAC,MAAM,EAAE;gBACX,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;aACnC;YAED,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAA,EAAE;gBACvD,MAAM,IAAI,KAAK,CAAC,UAAU,SAAS,6BAA6B,IAAI,CAAC,KAAK,IAAI,CAAC,CAAA;aAChF;YAED,OAAO,IAAA,kCAAU,EACf,GAAG,EAAE,CAAC,cAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EACtC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;gBAC1C,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAA;gBAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;gBAE1B,IAAI,SAAS,MAAK,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,SAAS,CAAA,EAAE;oBACnC,OAAO,KAAK,CAAA;iBACb;gBAED,kCAAkC;gBAClC,MAAM,aAAa,GAAS,MAAM,IAAA,uBAAa,EAAC,gBAAI,CAAC,CAAC,OAAO,CAAC;oBAC5D,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;oBACtB,SAAS,EAAE,CAAC,OAAO,CAAC;iBACrB,CAAC,CAAA;gBAEF,OAAO,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,iBAAiB,CAAC,CAAA;YACxE,CAAC,CACF,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;QAC3B,CAAC;KACF,CAAC;IACO,WAAA,IAAA,mBAAI,GAAE,CAAA;;;oCAA4D,kBAAO;kDAEjF;AAvCU,mBAAmB;IAD/B,IAAA,uBAAQ,EAAC,kBAAO,CAAC;GACL,mBAAmB,CAwC/B;AAxCY,kDAAmB"}
|
@@ -4,6 +4,7 @@ exports.resolvers = exports.entities = void 0;
|
|
4
4
|
const data_ooc_1 = require("./data-ooc");
|
5
5
|
const data_ooc_mutation_1 = require("./data-ooc-mutation");
|
6
6
|
const data_ooc_query_1 = require("./data-ooc-query");
|
7
|
+
const data_ooc_subscription_1 = require("./data-ooc-subscription");
|
7
8
|
exports.entities = [data_ooc_1.DataOoc];
|
8
|
-
exports.resolvers = [data_ooc_query_1.DataOocQuery, data_ooc_mutation_1.DataOocMutation];
|
9
|
+
exports.resolvers = [data_ooc_query_1.DataOocQuery, data_ooc_mutation_1.DataOocMutation, data_ooc_subscription_1.DataOocSubscription];
|
9
10
|
//# sourceMappingURL=index.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/data-ooc/index.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AACpC,2DAAqD;AACrD,qDAA+C;
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/data-ooc/index.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AACpC,2DAAqD;AACrD,qDAA+C;AAC/C,mEAA6D;AAEhD,QAAA,QAAQ,GAAG,CAAC,kBAAO,CAAC,CAAA;AACpB,QAAA,SAAS,GAAG,CAAC,6BAAY,EAAE,mCAAe,EAAE,2CAAmB,CAAC,CAAA"}
|
@@ -11,94 +11,16 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
11
11
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
13
13
|
};
|
14
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
15
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
16
|
-
};
|
17
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
18
15
|
exports.DataSampleMutation = void 0;
|
19
16
|
const type_graphql_1 = require("type-graphql");
|
20
|
-
const data_item_1 = require("../data-item/data-item");
|
21
|
-
const data_set_1 = require("../data-set/data-set");
|
22
17
|
const data_sample_1 = require("./data-sample");
|
23
18
|
const data_sample_type_1 = require("./data-sample-type");
|
24
|
-
const
|
25
|
-
const controllers_1 = require("../../controllers");
|
26
|
-
const data_ooc_1 = require("../data-ooc/data-ooc");
|
19
|
+
const create_data_sample_1 = require("../../controllers/create-data-sample");
|
27
20
|
const debug = require('debug')('things-factory:dataset:data-sample:data-sample-mutation');
|
28
|
-
// parse variable javascript string pattern
|
29
|
-
const replaceVariables = (keys, dic) => {
|
30
|
-
for (const k in keys) {
|
31
|
-
const matches = keys[k].match(/\$\{\w*\}/g);
|
32
|
-
matches &&
|
33
|
-
matches.forEach(m => {
|
34
|
-
keys[k] = keys[k].replace(m, dic[m.slice(2, -1)]);
|
35
|
-
});
|
36
|
-
}
|
37
|
-
return keys;
|
38
|
-
};
|
39
|
-
// It is required UTC date for Partitioning File System like AWS S3 from Athena.
|
40
|
-
// ex) %YYYY, %MM, %DD
|
41
|
-
const formatDate = (keys, _moment) => {
|
42
|
-
for (const k in keys) {
|
43
|
-
const matches = keys[k].match(/%\w*/g);
|
44
|
-
matches &&
|
45
|
-
matches.forEach(m => {
|
46
|
-
keys[k] = keys[k].replace(m, _moment.format(m.substr(1)));
|
47
|
-
});
|
48
|
-
}
|
49
|
-
return keys;
|
50
|
-
};
|
51
21
|
let DataSampleMutation = class DataSampleMutation {
|
52
22
|
async createDataSample(dataSample, context) {
|
53
|
-
|
54
|
-
const dataSet = await tx.getRepository(data_set_1.DataSet).findOne({
|
55
|
-
where: { id: dataSample.dataSet.id }
|
56
|
-
});
|
57
|
-
const dataItems = await tx.getRepository(data_item_1.DataItem).find({
|
58
|
-
where: {
|
59
|
-
domain,
|
60
|
-
dataSet
|
61
|
-
},
|
62
|
-
order: {
|
63
|
-
sequence: 'DESC'
|
64
|
-
}
|
65
|
-
});
|
66
|
-
const spec = dataItems.reduce((spec, dataItem) => {
|
67
|
-
spec[dataItem.tag] = Object.assign(Object.assign({}, dataItem.spec), { name: dataItem.name /* do we need ? */ });
|
68
|
-
return spec;
|
69
|
-
}, {});
|
70
|
-
// appending spec to data for reporting and optimizing AWS Athena search
|
71
|
-
dataSample.data = Object.assign(Object.assign({}, dataSample.data), { _spec: JSON.stringify(spec) });
|
72
|
-
var partitionKeys = Object.assign({}, dataSet.partitionKeys);
|
73
|
-
const collectedAt = dataSample.collectedAt || new Date();
|
74
|
-
partitionKeys = formatDate(partitionKeys, (0, moment_1.default)(collectedAt).utc());
|
75
|
-
partitionKeys = replaceVariables(partitionKeys, Object.assign({ domain: domain.subdomain, dataSetId: dataSample.dataSet.id }, dataSample.data));
|
76
|
-
const { ooc, oos } = controllers_1.DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {};
|
77
|
-
const result = await tx.getRepository(data_sample_1.DataSample).save(Object.assign(Object.assign({ name: dataSet.name, description: dataSet.description }, dataSample), { domain,
|
78
|
-
partitionKeys,
|
79
|
-
spec,
|
80
|
-
ooc,
|
81
|
-
oos,
|
82
|
-
collectedAt, creator: user, updater: user }));
|
83
|
-
if (ooc || oos) {
|
84
|
-
await tx.getRepository(data_ooc_1.DataOoc).save({
|
85
|
-
name: dataSet.name,
|
86
|
-
description: dataSet.description,
|
87
|
-
dataSet,
|
88
|
-
dataSample: result,
|
89
|
-
data: dataSample.data,
|
90
|
-
domain,
|
91
|
-
partitionKeys,
|
92
|
-
spec,
|
93
|
-
ooc,
|
94
|
-
oos,
|
95
|
-
state: data_ooc_1.DataOocStatus.CREATED,
|
96
|
-
collectedAt,
|
97
|
-
creator: user,
|
98
|
-
updater: user
|
99
|
-
});
|
100
|
-
}
|
101
|
-
return result;
|
23
|
+
return await (0, create_data_sample_1.createDataSample)(dataSample, context);
|
102
24
|
}
|
103
25
|
};
|
104
26
|
__decorate([
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"data-sample-mutation.js","sourceRoot":"","sources":["../../../server/service/data-sample/data-sample-mutation.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"data-sample-mutation.js","sourceRoot":"","sources":["../../../server/service/data-sample/data-sample-mutation.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,+CAAsE;AAEtE,+CAA0C;AAC1C,yDAAkD;AAElD,6EAAuE;AAEvE,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,yDAAyD,CAAC,CAAA;AAGzF,IAAa,kBAAkB,GAA/B,MAAa,kBAAkB;IAI7B,KAAK,CAAC,gBAAgB,CAAoB,UAAyB,EAAS,OAAY;QACtF,OAAO,MAAM,IAAA,qCAAgB,EAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACpD,CAAC;CACF,CAAA;AAHC;IAHC,IAAA,wBAAS,EAAC,sFAAsF,CAAC;IACjG,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,wBAAU,EAAE,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;IACtD,WAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IAA6B,WAAA,IAAA,kBAAG,GAAE,CAAA;;qCAArB,gCAAa;;0DAElE;AANU,kBAAkB;IAD9B,IAAA,uBAAQ,EAAC,wBAAU,CAAC;GACR,kBAAkB,CAO9B;AAPY,gDAAkB"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@things-factory/dataset",
|
3
|
-
"version": "5.0.0-alpha.
|
3
|
+
"version": "5.0.0-alpha.17",
|
4
4
|
"main": "dist-server/index.js",
|
5
5
|
"browser": "client/index.js",
|
6
6
|
"things-factory": true,
|
@@ -24,19 +24,19 @@
|
|
24
24
|
"migration:create": "node ../../node_modules/typeorm/cli.js migration:create -d ./server/migrations"
|
25
25
|
},
|
26
26
|
"dependencies": {
|
27
|
-
"@operato/app": "1.0.0-alpha.
|
28
|
-
"@operato/data-grist": "1.0.0-alpha.
|
29
|
-
"@operato/dataset": "1.0.0-alpha.
|
30
|
-
"@operato/graphql": "1.0.0-alpha.
|
31
|
-
"@operato/i18n": "1.0.0-alpha.
|
32
|
-
"@operato/layout": "1.0.0-alpha.
|
33
|
-
"@operato/shell": "1.0.0-alpha.
|
34
|
-
"@operato/styles": "1.0.0-alpha.
|
35
|
-
"@operato/utils": "1.0.0-alpha.
|
36
|
-
"@things-factory/auth-base": "^5.0.0-alpha.
|
37
|
-
"@things-factory/env": "^5.0.0-alpha.
|
38
|
-
"@things-factory/shell": "^5.0.0-alpha.
|
27
|
+
"@operato/app": "1.0.0-alpha.36",
|
28
|
+
"@operato/data-grist": "1.0.0-alpha.36",
|
29
|
+
"@operato/dataset": "1.0.0-alpha.36",
|
30
|
+
"@operato/graphql": "1.0.0-alpha.36",
|
31
|
+
"@operato/i18n": "1.0.0-alpha.36",
|
32
|
+
"@operato/layout": "1.0.0-alpha.36",
|
33
|
+
"@operato/shell": "1.0.0-alpha.36",
|
34
|
+
"@operato/styles": "1.0.0-alpha.36",
|
35
|
+
"@operato/utils": "1.0.0-alpha.36",
|
36
|
+
"@things-factory/auth-base": "^5.0.0-alpha.17",
|
37
|
+
"@things-factory/env": "^5.0.0-alpha.17",
|
38
|
+
"@things-factory/shell": "^5.0.0-alpha.17",
|
39
39
|
"moment": "^2.29.1"
|
40
40
|
},
|
41
|
-
"gitHead": "
|
41
|
+
"gitHead": "258d2d2f3bfb96ebcc88525c1cc0dcd8ea8d8c64"
|
42
42
|
}
|
@@ -0,0 +1,139 @@
|
|
1
|
+
import { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'
|
2
|
+
import { Domain, getRedirectSubdomainPath, pubsub } from '@things-factory/shell'
|
3
|
+
|
4
|
+
import { DataItem } from '../service/data-item/data-item'
|
5
|
+
import { DataSample } from '../service/data-sample/data-sample'
|
6
|
+
import { DataSet } from '../service/data-set/data-set'
|
7
|
+
import { DataUseCase } from './data-use-case'
|
8
|
+
import { EntityManager } from 'typeorm'
|
9
|
+
import { NewDataSample } from '../service/data-sample/data-sample-type'
|
10
|
+
import { User } from '@things-factory/auth-base'
|
11
|
+
import moment from 'moment'
|
12
|
+
|
13
|
+
const debug = require('debug')('things-factory:dataset:controller/save-data-sample')
|
14
|
+
|
15
|
+
// parse variable javascript string pattern
|
16
|
+
const replaceVariables = (keys, dic) => {
|
17
|
+
for (const k in keys) {
|
18
|
+
const matches = keys[k].match(/\$\{\w*\}/g)
|
19
|
+
matches &&
|
20
|
+
matches.forEach(m => {
|
21
|
+
keys[k] = keys[k].replace(m, dic[m.slice(2, -1)])
|
22
|
+
})
|
23
|
+
}
|
24
|
+
return keys
|
25
|
+
}
|
26
|
+
|
27
|
+
// It is required UTC date for Partitioning File System like AWS S3 from Athena.
|
28
|
+
// ex) %YYYY, %MM, %DD
|
29
|
+
const formatDate = (keys, _moment) => {
|
30
|
+
for (const k in keys) {
|
31
|
+
const matches = keys[k].match(/%\w*/g)
|
32
|
+
matches &&
|
33
|
+
matches.forEach(m => {
|
34
|
+
keys[k] = keys[k].replace(m, _moment.format(m.substr(1)))
|
35
|
+
})
|
36
|
+
}
|
37
|
+
return keys
|
38
|
+
}
|
39
|
+
|
40
|
+
export async function createDataSample(
|
41
|
+
dataSample: NewDataSample,
|
42
|
+
context: {
|
43
|
+
state: {
|
44
|
+
domain: Domain
|
45
|
+
user: User
|
46
|
+
tx: EntityManager
|
47
|
+
}
|
48
|
+
}
|
49
|
+
): Promise<DataSample> {
|
50
|
+
const { domain, user, tx } = context.state
|
51
|
+
|
52
|
+
const dataSet = await tx.getRepository(DataSet).findOne({
|
53
|
+
where: { id: dataSample.dataSet.id }
|
54
|
+
})
|
55
|
+
|
56
|
+
const dataItems = await tx.getRepository(DataItem).find({
|
57
|
+
where: {
|
58
|
+
domain,
|
59
|
+
dataSet
|
60
|
+
},
|
61
|
+
order: {
|
62
|
+
sequence: 'DESC'
|
63
|
+
}
|
64
|
+
})
|
65
|
+
|
66
|
+
const spec = dataItems.reduce((spec, dataItem) => {
|
67
|
+
spec[dataItem.tag] = {
|
68
|
+
...dataItem.spec,
|
69
|
+
name: dataItem.name /* do we need ? */
|
70
|
+
}
|
71
|
+
|
72
|
+
return spec
|
73
|
+
}, {})
|
74
|
+
|
75
|
+
var partitionKeys = {
|
76
|
+
...dataSet.partitionKeys
|
77
|
+
}
|
78
|
+
|
79
|
+
const collectedAt = dataSample.collectedAt || new Date()
|
80
|
+
partitionKeys = formatDate(partitionKeys, moment(collectedAt).utc())
|
81
|
+
partitionKeys = replaceVariables(partitionKeys, {
|
82
|
+
domain: domain.subdomain,
|
83
|
+
dataSetId: dataSample.dataSet.id,
|
84
|
+
...dataSample.data
|
85
|
+
})
|
86
|
+
|
87
|
+
const { ooc, oos } = DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {}
|
88
|
+
|
89
|
+
const result = await tx.getRepository(DataSample).save({
|
90
|
+
name: dataSet.name,
|
91
|
+
description: dataSet.description,
|
92
|
+
...dataSample,
|
93
|
+
domain,
|
94
|
+
partitionKeys,
|
95
|
+
spec,
|
96
|
+
ooc,
|
97
|
+
oos,
|
98
|
+
collectedAt,
|
99
|
+
creator: user,
|
100
|
+
updater: user
|
101
|
+
})
|
102
|
+
|
103
|
+
if (ooc || oos) {
|
104
|
+
const dataOoc = await tx.getRepository(DataOoc).save({
|
105
|
+
name: dataSet.name,
|
106
|
+
description: dataSet.description,
|
107
|
+
dataSet,
|
108
|
+
dataSample: result,
|
109
|
+
data: dataSample.data,
|
110
|
+
domain,
|
111
|
+
partitionKeys,
|
112
|
+
spec,
|
113
|
+
ooc,
|
114
|
+
oos,
|
115
|
+
state: DataOocStatus.CREATED,
|
116
|
+
collectedAt,
|
117
|
+
creator: user,
|
118
|
+
updater: user
|
119
|
+
})
|
120
|
+
|
121
|
+
pubsub.publish('data-ooc', {
|
122
|
+
dataOoc,
|
123
|
+
supervisoryRoleId: dataSet.supervisoryRoleId
|
124
|
+
})
|
125
|
+
|
126
|
+
pubsub.publish('notification', {
|
127
|
+
notification: {
|
128
|
+
domain,
|
129
|
+
type: 'error',
|
130
|
+
title: `Data OOC occurred on '${dataSet.name}'`,
|
131
|
+
body: `Data OOC occurred on '${dataSet.name}'`,
|
132
|
+
url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
|
133
|
+
timestamp: collectedAt
|
134
|
+
}
|
135
|
+
})
|
136
|
+
}
|
137
|
+
|
138
|
+
return result
|
139
|
+
}
|
package/server/routes.ts
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
import { getConnection } from 'typeorm'
|
2
|
-
|
3
|
-
import { DataSample } from './service'
|
4
1
|
import { DataItem } from './service/data-item/data-item'
|
5
2
|
import { DataSensor } from './service/data-sensor/data-sensor'
|
3
|
+
import { createDataSample } from './controllers/create-data-sample'
|
4
|
+
import { getConnection } from 'typeorm'
|
6
5
|
|
7
6
|
const debug = require('debug')('things-factory:dataset:routes')
|
8
7
|
|
@@ -59,26 +58,16 @@ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRout
|
|
59
58
|
}
|
60
59
|
})
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
partitionKeys: dataSet.partitionKeys,
|
73
|
-
dataSet,
|
74
|
-
data,
|
75
|
-
rawData,
|
76
|
-
spec,
|
77
|
-
source: deviceId,
|
78
|
-
collectedAt: new Date(timestamp),
|
79
|
-
creator: user,
|
80
|
-
updater: user
|
81
|
-
})
|
61
|
+
return await createDataSample(
|
62
|
+
{
|
63
|
+
dataSet,
|
64
|
+
data,
|
65
|
+
rawData,
|
66
|
+
source: deviceId,
|
67
|
+
collectedAt: new Date(timestamp)
|
68
|
+
},
|
69
|
+
{ state: { domain, user, tx } }
|
70
|
+
)
|
82
71
|
})
|
83
72
|
|
84
73
|
return 'OK'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import { Resolver, Subscription, Root } from 'type-graphql'
|
2
|
+
import { withFilter } from 'graphql-subscriptions'
|
3
|
+
import { pubsub } from '@things-factory/shell'
|
4
|
+
import { User } from '@things-factory/auth-base'
|
5
|
+
import { DataOoc } from './data-ooc'
|
6
|
+
import { getRepository } from 'typeorm'
|
7
|
+
|
8
|
+
const debug = require('debug')('things-factory:dataset:data-ooc-subscription')
|
9
|
+
|
10
|
+
@Resolver(DataOoc)
|
11
|
+
export class DataOocSubscription {
|
12
|
+
@Subscription({
|
13
|
+
subscribe: (_, args, context, info) => {
|
14
|
+
const { domain, user } = context.state
|
15
|
+
const subdomain = domain?.subdomain
|
16
|
+
|
17
|
+
debug('subscribe', subdomain)
|
18
|
+
|
19
|
+
if (!domain) {
|
20
|
+
throw new Error('domain required')
|
21
|
+
}
|
22
|
+
|
23
|
+
if (!user.domains?.find(d => d.subdomain === subdomain)) {
|
24
|
+
throw new Error(`domain(${subdomain}) is not working for user(${user.email}).`)
|
25
|
+
}
|
26
|
+
|
27
|
+
return withFilter(
|
28
|
+
() => pubsub.asyncIterator('data-ooc'),
|
29
|
+
async (payload, variables, context, info) => {
|
30
|
+
const { dataOoc, supervisoryRoleId } = payload
|
31
|
+
const { domain } = dataOoc
|
32
|
+
|
33
|
+
if (subdomain !== domain?.subdomain) {
|
34
|
+
return false
|
35
|
+
}
|
36
|
+
|
37
|
+
// check if the user has that role
|
38
|
+
const userWithRoles: User = await getRepository(User).findOne({
|
39
|
+
where: { id: user.id },
|
40
|
+
relations: ['roles']
|
41
|
+
})
|
42
|
+
|
43
|
+
return userWithRoles.roles.find(role => role.id === supervisoryRoleId)
|
44
|
+
}
|
45
|
+
)(_, args, context, info)
|
46
|
+
}
|
47
|
+
})
|
48
|
+
dataOoc(@Root() payload: { dataOoc: DataOoc; supervisoryRoleId: string }): DataOoc {
|
49
|
+
return payload.dataOoc
|
50
|
+
}
|
51
|
+
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { DataOoc } from './data-ooc'
|
2
2
|
import { DataOocMutation } from './data-ooc-mutation'
|
3
3
|
import { DataOocQuery } from './data-ooc-query'
|
4
|
+
import { DataOocSubscription } from './data-ooc-subscription'
|
4
5
|
|
5
6
|
export const entities = [DataOoc]
|
6
|
-
export const resolvers = [DataOocQuery, DataOocMutation]
|
7
|
+
export const resolvers = [DataOocQuery, DataOocMutation, DataOocSubscription]
|
@@ -1,121 +1,18 @@
|
|
1
1
|
import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
|
2
2
|
|
3
|
-
import { DataItem } from '../data-item/data-item'
|
4
|
-
import { DataSet } from '../data-set/data-set'
|
5
3
|
import { DataSample } from './data-sample'
|
6
4
|
import { NewDataSample } from './data-sample-type'
|
7
5
|
|
8
|
-
import
|
9
|
-
import { DataUseCase } from '../../controllers'
|
10
|
-
import { DataOoc, DataOocStatus } from '../data-ooc/data-ooc'
|
6
|
+
import { createDataSample } from '../../controllers/create-data-sample'
|
11
7
|
|
12
8
|
const debug = require('debug')('things-factory:dataset:data-sample:data-sample-mutation')
|
13
9
|
|
14
|
-
// parse variable javascript string pattern
|
15
|
-
const replaceVariables = (keys, dic) => {
|
16
|
-
for (const k in keys) {
|
17
|
-
const matches = keys[k].match(/\$\{\w*\}/g)
|
18
|
-
matches &&
|
19
|
-
matches.forEach(m => {
|
20
|
-
keys[k] = keys[k].replace(m, dic[m.slice(2, -1)])
|
21
|
-
})
|
22
|
-
}
|
23
|
-
return keys
|
24
|
-
}
|
25
|
-
|
26
|
-
// It is required UTC date for Partitioning File System like AWS S3 from Athena.
|
27
|
-
// ex) %YYYY, %MM, %DD
|
28
|
-
const formatDate = (keys, _moment) => {
|
29
|
-
for (const k in keys) {
|
30
|
-
const matches = keys[k].match(/%\w*/g)
|
31
|
-
matches &&
|
32
|
-
matches.forEach(m => {
|
33
|
-
keys[k] = keys[k].replace(m, _moment.format(m.substr(1)))
|
34
|
-
})
|
35
|
-
}
|
36
|
-
return keys
|
37
|
-
}
|
38
|
-
|
39
10
|
@Resolver(DataSample)
|
40
11
|
export class DataSampleMutation {
|
41
12
|
@Directive('@privilege(category: "data-sample", privilege: "mutation", domainOwnerGranted: true)')
|
42
13
|
@Directive('@transaction')
|
43
14
|
@Mutation(returns => DataSample, { description: 'To create new data sample' })
|
44
15
|
async createDataSample(@Arg('dataSample') dataSample: NewDataSample, @Ctx() context: any): Promise<DataSample> {
|
45
|
-
|
46
|
-
|
47
|
-
const dataSet = await tx.getRepository(DataSet).findOne({
|
48
|
-
where: { id: dataSample.dataSet.id }
|
49
|
-
})
|
50
|
-
const dataItems = await tx.getRepository(DataItem).find({
|
51
|
-
where: {
|
52
|
-
domain,
|
53
|
-
dataSet
|
54
|
-
},
|
55
|
-
order: {
|
56
|
-
sequence: 'DESC'
|
57
|
-
}
|
58
|
-
})
|
59
|
-
|
60
|
-
const spec = dataItems.reduce((spec, dataItem) => {
|
61
|
-
spec[dataItem.tag] = {
|
62
|
-
...dataItem.spec,
|
63
|
-
name: dataItem.name /* do we need ? */
|
64
|
-
}
|
65
|
-
|
66
|
-
return spec
|
67
|
-
}, {})
|
68
|
-
|
69
|
-
// appending spec to data for reporting and optimizing AWS Athena search
|
70
|
-
dataSample.data = { ...dataSample.data, _spec: JSON.stringify(spec) }
|
71
|
-
|
72
|
-
var partitionKeys = {
|
73
|
-
...dataSet.partitionKeys
|
74
|
-
}
|
75
|
-
|
76
|
-
const collectedAt = dataSample.collectedAt || new Date()
|
77
|
-
partitionKeys = formatDate(partitionKeys, moment(collectedAt).utc())
|
78
|
-
partitionKeys = replaceVariables(partitionKeys, {
|
79
|
-
domain: domain.subdomain,
|
80
|
-
dataSetId: dataSample.dataSet.id,
|
81
|
-
...dataSample.data
|
82
|
-
})
|
83
|
-
|
84
|
-
const { ooc, oos } = DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {}
|
85
|
-
|
86
|
-
const result = await tx.getRepository(DataSample).save({
|
87
|
-
name: dataSet.name,
|
88
|
-
description: dataSet.description,
|
89
|
-
...dataSample,
|
90
|
-
domain,
|
91
|
-
partitionKeys,
|
92
|
-
spec,
|
93
|
-
ooc,
|
94
|
-
oos,
|
95
|
-
collectedAt,
|
96
|
-
creator: user,
|
97
|
-
updater: user
|
98
|
-
})
|
99
|
-
|
100
|
-
if (ooc || oos) {
|
101
|
-
await tx.getRepository(DataOoc).save({
|
102
|
-
name: dataSet.name,
|
103
|
-
description: dataSet.description,
|
104
|
-
dataSet,
|
105
|
-
dataSample: result,
|
106
|
-
data: dataSample.data,
|
107
|
-
domain,
|
108
|
-
partitionKeys,
|
109
|
-
spec,
|
110
|
-
ooc,
|
111
|
-
oos,
|
112
|
-
state: DataOocStatus.CREATED,
|
113
|
-
collectedAt,
|
114
|
-
creator: user,
|
115
|
-
updater: user
|
116
|
-
})
|
117
|
-
}
|
118
|
-
|
119
|
-
return result
|
16
|
+
return await createDataSample(dataSample, context)
|
120
17
|
}
|
121
18
|
}
|
package/translations/ms.json
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
"field.ref-by": "ref. by",
|
19
19
|
"field.serial-no": "serial #",
|
20
20
|
"field.spec": "spec",
|
21
|
-
"field.state": "
|
21
|
+
"field.state": "keadaan",
|
22
22
|
"field.supervisory-role": "supervisory role",
|
23
23
|
"field.tag": "tag name",
|
24
24
|
"field.unit": "unit",
|
@@ -30,4 +30,4 @@
|
|
30
30
|
"title.data-sample list": "data sample list",
|
31
31
|
"title.data-sensor list": "data sensor list",
|
32
32
|
"title.data-set list": "data set list"
|
33
|
-
}
|
33
|
+
}
|
package/translations/zh.json
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
"field.ref-by": "ref. by",
|
19
19
|
"field.serial-no": "serial #",
|
20
20
|
"field.spec": "spec",
|
21
|
-
"field.state": "
|
21
|
+
"field.state": "状态",
|
22
22
|
"field.supervisory-role": "supervisory role",
|
23
23
|
"field.tag": "tag name",
|
24
24
|
"field.unit": "unit",
|
@@ -30,4 +30,4 @@
|
|
30
30
|
"title.data-sample list": "data sample list",
|
31
31
|
"title.data-sensor list": "data sensor list",
|
32
32
|
"title.data-set list": "data set list"
|
33
|
-
}
|
33
|
+
}
|