@things-factory/dataset 5.0.0-alpha.14 → 5.0.0-alpha.18
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-item-list.js +1 -1
- package/client/pages/data-ooc.js +35 -3
- package/client/pages/data-set.js +2 -2
- package/dist-server/controllers/create-data-sample.js +103 -0
- package/dist-server/controllers/create-data-sample.js.map +1 -0
- package/dist-server/controllers/data-use-case.js +3 -3
- package/dist-server/controllers/data-use-case.js.map +1 -1
- package/dist-server/routes.js +9 -24
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/data-ooc/data-ooc-query.js +27 -1
- package/dist-server/service/data-ooc/data-ooc-query.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/dist-server/service/data-sample/data-sample.js +3 -3
- package/dist-server/service/data-sample/data-sample.js.map +1 -1
- package/dist-server/service/data-set/data-set-query.js +12 -0
- package/dist-server/service/data-set/data-set-query.js.map +1 -1
- package/dist-server/service/data-set/data-set.js +3 -3
- package/dist-server/service/data-set/data-set.js.map +1 -1
- package/package.json +14 -14
- package/server/controllers/create-data-sample.ts +142 -0
- package/server/controllers/data-use-case.ts +3 -3
- package/server/routes.ts +18 -32
- package/server/service/data-ooc/data-ooc-query.ts +20 -2
- 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/server/service/data-sample/data-sample.ts +4 -3
- package/server/service/data-set/data-set-query.ts +8 -1
- package/server/service/data-set/data-set.ts +4 -3
- package/translations/en.json +2 -1
- package/translations/ko.json +2 -1
- package/translations/ms.json +2 -1
- package/translations/zh.json +2 -1
@@ -11,10 +11,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
11
11
|
var _a, _b, _c, _d, _e;
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
13
13
|
exports.DataSet = void 0;
|
14
|
-
const typeorm_1 = require("typeorm");
|
15
|
-
const shell_1 = require("@things-factory/shell");
|
16
14
|
const type_graphql_1 = require("type-graphql");
|
15
|
+
const typeorm_1 = require("typeorm");
|
17
16
|
const auth_base_1 = require("@things-factory/auth-base");
|
17
|
+
const shell_1 = require("@things-factory/shell");
|
18
18
|
const data_item_1 = require("../data-item/data-item");
|
19
19
|
let DataSet = class DataSet {
|
20
20
|
};
|
@@ -55,7 +55,7 @@ __decorate([
|
|
55
55
|
(0, typeorm_1.ManyToOne)(type => auth_base_1.Role, {
|
56
56
|
nullable: true
|
57
57
|
}),
|
58
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
58
|
+
(0, type_graphql_1.Field)(type => auth_base_1.Role, { nullable: true }),
|
59
59
|
__metadata("design:type", typeof (_b = typeof auth_base_1.Role !== "undefined" && auth_base_1.Role) === "function" ? _b : Object)
|
60
60
|
], DataSet.prototype, "supervisoryRole", void 0);
|
61
61
|
__decorate([
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"data-set.js","sourceRoot":"","sources":["../../../server/service/data-set/data-set.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA
|
1
|
+
{"version":3,"file":"data-set.js","sourceRoot":"","sources":["../../../server/service/data-set/data-set.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,+CAAoD;AACpD,qCAUgB;AAEhB,yDAAsD;AACtD,iDAA4D;AAE5D,sDAAiD;AAKjD,IAAa,OAAO,GAApB,MAAa,OAAO;CAoGnB,CAAA;AAjGC;IAFC,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,CAAC;;mCACC;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;kDACjB,cAAM,oBAAN,cAAM;uCAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;;yCAChC;AAIjB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,GAAE;;qCACI;AAMZ;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACN;AAMpB;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCACV;AAMhB;IAJC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE;QACvB,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;kDACtB,gBAAI,oBAAJ,gBAAI;gDAAA;AAGtB;IADC,IAAA,oBAAU,EAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC;;kDAChC;AAM1B;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qCACY;AAMtC;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCACV;AAIhB;IAFC,IAAA,gBAAM,EAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACzC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;kDAChC,oBAAY,oBAAZ,oBAAY;8CAAA;AAM5B;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCACV;AAIhB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzD,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACzB;AAMrB;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACT;AAMjB;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACT;AAIjB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACd,IAAI;0CAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACd,IAAI;0CAAA;AAMhB;IAJC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE;QACvB,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;kDAChB,gBAAI,oBAAJ,gBAAI;wCAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;;0CAChC;AAMlB;IAJC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE;QACvB,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;kDAChB,gBAAI,oBAAJ,gBAAI;wCAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;;0CAChC;AAnGP,OAAO;IAHnB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,eAAe,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC9F,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;GACrC,OAAO,CAoGnB;AApGY,0BAAO"}
|
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.18",
|
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.37",
|
28
|
+
"@operato/data-grist": "1.0.0-alpha.37",
|
29
|
+
"@operato/dataset": "1.0.0-alpha.37",
|
30
|
+
"@operato/graphql": "1.0.0-alpha.37",
|
31
|
+
"@operato/i18n": "1.0.0-alpha.37",
|
32
|
+
"@operato/layout": "1.0.0-alpha.37",
|
33
|
+
"@operato/shell": "1.0.0-alpha.37",
|
34
|
+
"@operato/styles": "1.0.0-alpha.37",
|
35
|
+
"@operato/utils": "1.0.0-alpha.37",
|
36
|
+
"@things-factory/auth-base": "^5.0.0-alpha.18",
|
37
|
+
"@things-factory/env": "^5.0.0-alpha.18",
|
38
|
+
"@things-factory/shell": "^5.0.0-alpha.18",
|
39
39
|
"moment": "^2.29.1"
|
40
40
|
},
|
41
|
-
"gitHead": "
|
41
|
+
"gitHead": "724366d140177b67a583e21d43a4bc6e25ada3e8"
|
42
42
|
}
|
@@ -0,0 +1,142 @@
|
|
1
|
+
import moment from 'moment'
|
2
|
+
import { EntityManager } from 'typeorm'
|
3
|
+
|
4
|
+
import { User } from '@things-factory/auth-base'
|
5
|
+
import { Domain, getRedirectSubdomainPath, pubsub } from '@things-factory/shell'
|
6
|
+
|
7
|
+
import { DataItem } from '../service/data-item/data-item'
|
8
|
+
import { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'
|
9
|
+
import { DataSample } from '../service/data-sample/data-sample'
|
10
|
+
import { NewDataSample } from '../service/data-sample/data-sample-type'
|
11
|
+
import { DataSet } from '../service/data-set/data-set'
|
12
|
+
import { DataUseCase } from './data-use-case'
|
13
|
+
|
14
|
+
const debug = require('debug')('things-factory:dataset:controller/save-data-sample')
|
15
|
+
|
16
|
+
// parse variable javascript string pattern
|
17
|
+
const replaceVariables = (keys, dic) => {
|
18
|
+
for (const k in keys) {
|
19
|
+
const matches = keys[k].match(/\$\{\w*\}/g)
|
20
|
+
matches &&
|
21
|
+
matches.forEach(m => {
|
22
|
+
keys[k] = keys[k].replace(m, dic[m.slice(2, -1)])
|
23
|
+
})
|
24
|
+
}
|
25
|
+
return keys
|
26
|
+
}
|
27
|
+
|
28
|
+
// It is required UTC date for Partitioning File System like AWS S3 from Athena.
|
29
|
+
// ex) %YYYY, %MM, %DD
|
30
|
+
const formatDate = (keys, _moment) => {
|
31
|
+
for (const k in keys) {
|
32
|
+
const matches = keys[k].match(/%\w*/g)
|
33
|
+
matches &&
|
34
|
+
matches.forEach(m => {
|
35
|
+
keys[k] = keys[k].replace(m, _moment.format(m.substr(1)))
|
36
|
+
})
|
37
|
+
}
|
38
|
+
return keys
|
39
|
+
}
|
40
|
+
|
41
|
+
export async function createDataSample(
|
42
|
+
dataSample: NewDataSample,
|
43
|
+
context: {
|
44
|
+
state: {
|
45
|
+
domain: Domain
|
46
|
+
user: User
|
47
|
+
tx: EntityManager
|
48
|
+
}
|
49
|
+
}
|
50
|
+
): Promise<DataSample> {
|
51
|
+
const { domain, user, tx } = context.state
|
52
|
+
|
53
|
+
const dataSet = await tx.getRepository(DataSet).findOne({
|
54
|
+
where: { id: dataSample.dataSet.id }
|
55
|
+
})
|
56
|
+
|
57
|
+
const dataItems = await tx.getRepository(DataItem).find({
|
58
|
+
where: {
|
59
|
+
domain,
|
60
|
+
dataSet
|
61
|
+
},
|
62
|
+
order: {
|
63
|
+
sequence: 'DESC'
|
64
|
+
}
|
65
|
+
})
|
66
|
+
|
67
|
+
const spec = dataItems.reduce((spec, dataItem) => {
|
68
|
+
spec[dataItem.tag] = {
|
69
|
+
...dataItem.spec,
|
70
|
+
name: dataItem.name /* do we need ? */
|
71
|
+
}
|
72
|
+
|
73
|
+
return spec
|
74
|
+
}, {})
|
75
|
+
|
76
|
+
var partitionKeys = {
|
77
|
+
...dataSet.partitionKeys
|
78
|
+
}
|
79
|
+
|
80
|
+
const collectedAt = dataSample.collectedAt || new Date()
|
81
|
+
partitionKeys = formatDate(partitionKeys, moment(collectedAt).utc())
|
82
|
+
partitionKeys = replaceVariables(partitionKeys, {
|
83
|
+
domain: domain.subdomain,
|
84
|
+
dataSetId: dataSample.dataSet.id,
|
85
|
+
...dataSample.data
|
86
|
+
})
|
87
|
+
|
88
|
+
const { ooc, oos } = DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {}
|
89
|
+
|
90
|
+
const result = await tx.getRepository(DataSample).save({
|
91
|
+
name: dataSet.name,
|
92
|
+
description: dataSet.description,
|
93
|
+
...dataSample,
|
94
|
+
domain,
|
95
|
+
partitionKeys,
|
96
|
+
spec,
|
97
|
+
ooc,
|
98
|
+
oos,
|
99
|
+
collectedAt,
|
100
|
+
creator: user,
|
101
|
+
updater: user
|
102
|
+
})
|
103
|
+
|
104
|
+
if (ooc || oos) {
|
105
|
+
const dataOoc = await tx.getRepository(DataOoc).save({
|
106
|
+
name: dataSet.name,
|
107
|
+
description: dataSet.description,
|
108
|
+
dataSet,
|
109
|
+
dataSample: result,
|
110
|
+
data: dataSample.data,
|
111
|
+
domain,
|
112
|
+
partitionKeys,
|
113
|
+
spec,
|
114
|
+
ooc,
|
115
|
+
oos,
|
116
|
+
state: DataOocStatus.CREATED,
|
117
|
+
collectedAt,
|
118
|
+
creator: user,
|
119
|
+
updater: user
|
120
|
+
})
|
121
|
+
|
122
|
+
debug('dataOoc is about to publish...', dataOoc)
|
123
|
+
|
124
|
+
pubsub.publish('data-ooc', {
|
125
|
+
dataOoc,
|
126
|
+
supervisoryRoleId: dataSet.supervisoryRoleId
|
127
|
+
})
|
128
|
+
|
129
|
+
pubsub.publish('notification', {
|
130
|
+
notification: {
|
131
|
+
domain,
|
132
|
+
type: 'error',
|
133
|
+
title: `Data OOC occurred on '${dataSet.name}'`,
|
134
|
+
body: `Data OOC occurred on '${dataSet.name}'`,
|
135
|
+
url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
|
136
|
+
timestamp: collectedAt
|
137
|
+
}
|
138
|
+
})
|
139
|
+
}
|
140
|
+
|
141
|
+
return result
|
142
|
+
}
|
@@ -43,12 +43,12 @@ export abstract class DataUseCase {
|
|
43
43
|
|
44
44
|
for (let i = 0; i < dataItems.length; i++) {
|
45
45
|
const dataItem = dataItems[i]
|
46
|
-
const {
|
47
|
-
if (!active) {
|
46
|
+
const { active, tag } = dataItem
|
47
|
+
if (!active || !tag) {
|
48
48
|
continue
|
49
49
|
}
|
50
50
|
|
51
|
-
let values: any | any[] = data[
|
51
|
+
let values: any | any[] = data[tag]
|
52
52
|
if (typeof values === 'undefined') {
|
53
53
|
continue // TODO what if in case no value ?
|
54
54
|
}
|
package/server/routes.ts
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
import { getConnection } from 'typeorm'
|
1
|
+
import { getConnection, getRepository } from 'typeorm'
|
2
2
|
|
3
|
-
import {
|
4
|
-
|
3
|
+
import { User } from '@things-factory/auth-base'
|
4
|
+
|
5
|
+
import { createDataSample } from './controllers/create-data-sample'
|
5
6
|
import { DataSensor } from './service/data-sensor/data-sensor'
|
6
7
|
|
7
8
|
const debug = require('debug')('things-factory:dataset:routes')
|
@@ -47,41 +48,26 @@ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRout
|
|
47
48
|
|
48
49
|
const domain = sensor.domain
|
49
50
|
const dataSet = sensor.dataSet
|
50
|
-
const user =
|
51
|
-
|
52
|
-
const dataItems = await tx.getRepository(DataItem).find({
|
51
|
+
const user: User = await getRepository(User).findOne({
|
53
52
|
where: {
|
54
|
-
|
55
|
-
|
56
|
-
},
|
57
|
-
order: {
|
58
|
-
sequence: 'DESC'
|
53
|
+
reference: sensor.appliance.id,
|
54
|
+
userType: 'appliance'
|
59
55
|
}
|
60
56
|
})
|
61
57
|
|
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
|
-
})
|
58
|
+
return await createDataSample(
|
59
|
+
{
|
60
|
+
dataSet,
|
61
|
+
data,
|
62
|
+
rawData,
|
63
|
+
source: deviceId,
|
64
|
+
collectedAt: new Date(timestamp)
|
65
|
+
},
|
66
|
+
{ state: { domain, user, tx } }
|
67
|
+
)
|
82
68
|
})
|
83
69
|
|
84
|
-
|
70
|
+
context.status = 200
|
85
71
|
})
|
86
72
|
})
|
87
73
|
|
@@ -1,7 +1,11 @@
|
|
1
|
-
import {
|
1
|
+
import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
|
2
2
|
import { getRepository } from 'typeorm'
|
3
|
-
|
3
|
+
|
4
4
|
import { User } from '@things-factory/auth-base'
|
5
|
+
import { convertListParams, Domain, ListParam } from '@things-factory/shell'
|
6
|
+
|
7
|
+
import { DataSample } from '../data-sample/data-sample'
|
8
|
+
import { DataSet } from '../data-set/data-set'
|
5
9
|
import { DataOoc } from './data-ooc'
|
6
10
|
import { DataOocList } from './data-ooc-type'
|
7
11
|
|
@@ -28,6 +32,20 @@ export class DataOocQuery {
|
|
28
32
|
return { items, total }
|
29
33
|
}
|
30
34
|
|
35
|
+
@FieldResolver(type => DataSet)
|
36
|
+
async dataSet(@Root() dataOoc: DataOoc): Promise<DataSet> {
|
37
|
+
return await getRepository(DataSet).findOne({
|
38
|
+
id: dataOoc.dataSetId
|
39
|
+
})
|
40
|
+
}
|
41
|
+
|
42
|
+
@FieldResolver(type => DataSample)
|
43
|
+
async dataSample(@Root() dataOoc: DataOoc): Promise<DataSample> {
|
44
|
+
return await getRepository(DataSample).findOne({
|
45
|
+
id: dataOoc.dataSampleId
|
46
|
+
})
|
47
|
+
}
|
48
|
+
|
31
49
|
@FieldResolver(type => Domain)
|
32
50
|
async domain(@Root() dataOoc: DataOoc): Promise<Domain> {
|
33
51
|
return await getRepository(Domain).findOne(dataOoc.domainId)
|
@@ -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
|
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { Field, ID, ObjectType } from 'type-graphql'
|
1
2
|
import {
|
2
3
|
Column,
|
3
4
|
CreateDateColumn,
|
@@ -8,12 +9,12 @@ import {
|
|
8
9
|
RelationId,
|
9
10
|
UpdateDateColumn
|
10
11
|
} from 'typeorm'
|
11
|
-
import { Domain, ScalarObject } from '@things-factory/shell'
|
12
|
-
import { Field, ID, ObjectType } from 'type-graphql'
|
13
12
|
|
14
|
-
import { DataSet } from '../data-set/data-set'
|
15
13
|
import { User } from '@things-factory/auth-base'
|
16
14
|
import { config } from '@things-factory/env'
|
15
|
+
import { Domain, ScalarObject } from '@things-factory/shell'
|
16
|
+
|
17
|
+
import { DataSet } from '../data-set/data-set'
|
17
18
|
|
18
19
|
const ORMCONFIG = config.get('ormconfig', {})
|
19
20
|
const DATABASE_TYPE = ORMCONFIG.type
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
|
2
2
|
import { getRepository } from 'typeorm'
|
3
3
|
|
4
|
-
import { User } from '@things-factory/auth-base'
|
4
|
+
import { Role, User } from '@things-factory/auth-base'
|
5
5
|
import { convertListParams, Domain, ListParam } from '@things-factory/shell'
|
6
6
|
|
7
7
|
import { DataItem } from '../data-item/data-item'
|
@@ -38,6 +38,13 @@ export class DataSetQuery {
|
|
38
38
|
})
|
39
39
|
}
|
40
40
|
|
41
|
+
@FieldResolver(type => Role)
|
42
|
+
async supervisoryRole(@Root() dataSet: DataSet): Promise<Role> {
|
43
|
+
return await getRepository(Role).findOne({
|
44
|
+
id: dataSet.supervisoryRoleId
|
45
|
+
})
|
46
|
+
}
|
47
|
+
|
41
48
|
@FieldResolver(type => Domain)
|
42
49
|
async domain(@Root() dataSet: DataSet): Promise<Domain> {
|
43
50
|
return await getRepository(Domain).findOne(dataSet.domainId)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { Field, ID, ObjectType } from 'type-graphql'
|
1
2
|
import {
|
2
3
|
Column,
|
3
4
|
CreateDateColumn,
|
@@ -9,9 +10,9 @@ import {
|
|
9
10
|
RelationId,
|
10
11
|
UpdateDateColumn
|
11
12
|
} from 'typeorm'
|
12
|
-
|
13
|
-
import { Field, ID, ObjectType } from 'type-graphql'
|
13
|
+
|
14
14
|
import { Role, User } from '@things-factory/auth-base'
|
15
|
+
import { Domain, ScalarObject } from '@things-factory/shell'
|
15
16
|
|
16
17
|
import { DataItem } from '../data-item/data-item'
|
17
18
|
|
@@ -49,7 +50,7 @@ export class DataSet {
|
|
49
50
|
@ManyToOne(type => Role, {
|
50
51
|
nullable: true
|
51
52
|
})
|
52
|
-
@Field({ nullable: true })
|
53
|
+
@Field(type => Role, { nullable: true })
|
53
54
|
supervisoryRole?: Role
|
54
55
|
|
55
56
|
@RelationId((dataSet: DataSet) => dataSet.supervisoryRole)
|
package/translations/en.json
CHANGED
@@ -26,7 +26,8 @@
|
|
26
26
|
"text.data sample created successfully": "a data sample created successfully",
|
27
27
|
"title.data-entry-form": "data entry form",
|
28
28
|
"title.data-item list": "data item list",
|
29
|
+
"title.data-ooc list": "data OOC list",
|
29
30
|
"title.data-sample list": "data sample list",
|
30
31
|
"title.data-sensor list": "data sensor list",
|
31
32
|
"title.data-set list": "data set list"
|
32
|
-
}
|
33
|
+
}
|
package/translations/ko.json
CHANGED
@@ -26,7 +26,8 @@
|
|
26
26
|
"text.data sample created successfully": "데이타 샘플이 성공적으로 생성되었습니다",
|
27
27
|
"title.data-entry-form": "데이타 입력",
|
28
28
|
"title.data-item list": "데이타 아이템 조회",
|
29
|
+
"title.data-ooc list": "데이타 이탈점 조회",
|
29
30
|
"title.data-sample list": "데이타 샘플 조회",
|
30
31
|
"title.data-sensor list": "데이타 센서 조회",
|
31
32
|
"title.data-set list": "데이타 셋 조회"
|
32
|
-
}
|
33
|
+
}
|
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",
|
@@ -26,6 +26,7 @@
|
|
26
26
|
"text.data sample created successfully": "a data sample created successfully",
|
27
27
|
"title.data-entry-form": "data entry form",
|
28
28
|
"title.data-item list": "data item list",
|
29
|
+
"title.data-ooc list": "data OOC list",
|
29
30
|
"title.data-sample list": "data sample list",
|
30
31
|
"title.data-sensor list": "data sensor list",
|
31
32
|
"title.data-set list": "data set list"
|
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",
|
@@ -26,6 +26,7 @@
|
|
26
26
|
"text.data sample created successfully": "a data sample created successfully",
|
27
27
|
"title.data-entry-form": "data entry form",
|
28
28
|
"title.data-item list": "data item list",
|
29
|
+
"title.data-ooc list": "data OOC list",
|
29
30
|
"title.data-sample list": "data sample list",
|
30
31
|
"title.data-sensor list": "data sensor list",
|
31
32
|
"title.data-set list": "data set list"
|