@things-factory/dataset 5.0.0-alpha.8 → 5.0.0-zeta.0
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/README.md +9 -0
- package/assets/data-samples.jpg +0 -0
- package/client/bootstrap.js +21 -1
- package/client/pages/{data-entry-form.js → data-entry/data-entry-form.js} +15 -3
- package/client/pages/data-entry/data-entry-list-page.js +390 -0
- package/client/pages/data-ooc/data-ooc-list-page.js +416 -0
- package/client/pages/data-ooc/data-ooc-view.js +183 -0
- package/client/pages/data-report/data-report-embed-page.js +113 -0
- package/client/pages/data-report/data-report-list-page.js +432 -0
- package/client/pages/data-report/jasper-report-oocs-page.js +122 -0
- package/client/pages/data-report/jasper-report-samples-crosstab-page.js +122 -0
- package/client/pages/data-report/jasper-report-samples-page.js +122 -0
- package/client/pages/data-sample/data-sample-list-page.js +372 -0
- package/client/pages/data-sample/data-sample-view.js +98 -0
- package/client/pages/{data-sensor.js → data-sensor/data-sensor-list-page.js} +43 -68
- package/client/pages/{data-item-list.js → data-set/data-item-list.js} +36 -11
- package/client/pages/{data-set-importer.js → data-set/data-set-importer.js} +0 -0
- package/client/pages/data-set/data-set-list-page.js +739 -0
- package/client/route.js +34 -6
- package/config/config.development.js +13 -0
- package/config/config.production.js +1 -0
- package/dist-server/controllers/create-data-sample.js +133 -0
- package/dist-server/controllers/create-data-sample.js.map +1 -0
- package/dist-server/controllers/data-use-case.js +57 -0
- package/dist-server/controllers/data-use-case.js.map +1 -0
- package/dist-server/controllers/index.js +18 -0
- package/dist-server/controllers/index.js.map +1 -1
- package/dist-server/controllers/jasper-report.js +156 -0
- package/dist-server/controllers/jasper-report.js.map +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/routes.js +13 -24
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/data-item/data-item-mutation.js +5 -1
- package/dist-server/service/data-item/data-item-mutation.js.map +1 -1
- package/dist-server/service/data-item/data-item-query.js +6 -2
- package/dist-server/service/data-item/data-item-query.js.map +1 -1
- package/dist-server/service/data-item/data-item-type.js +15 -7
- package/dist-server/service/data-item/data-item-type.js.map +1 -1
- package/dist-server/service/data-item/data-item.js +17 -3
- package/dist-server/service/data-item/data-item.js.map +1 -1
- package/dist-server/service/data-ooc/data-ooc-mutation.js +92 -0
- package/dist-server/service/data-ooc/data-ooc-mutation.js.map +1 -0
- package/dist-server/service/data-ooc/data-ooc-query.js +120 -0
- package/dist-server/service/data-ooc/data-ooc-query.js.map +1 -0
- 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/data-ooc-type.js +107 -0
- package/dist-server/service/data-ooc/data-ooc-type.js.map +1 -0
- package/dist-server/service/data-ooc/data-ooc.js +237 -0
- package/dist-server/service/data-ooc/data-ooc.js.map +1 -0
- package/dist-server/service/data-ooc/index.js +10 -0
- package/dist-server/service/data-ooc/index.js.map +1 -0
- package/dist-server/service/data-sample/data-sample-mutation.js +2 -138
- package/dist-server/service/data-sample/data-sample-mutation.js.map +1 -1
- package/dist-server/service/data-sample/data-sample-query.js +7 -2
- package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
- package/dist-server/service/data-sample/data-sample-type.js +12 -42
- package/dist-server/service/data-sample/data-sample-type.js.map +1 -1
- package/dist-server/service/data-sample/data-sample.js +34 -3
- package/dist-server/service/data-sample/data-sample.js.map +1 -1
- package/dist-server/service/data-sensor/data-sensor-query.js +7 -2
- package/dist-server/service/data-sensor/data-sensor-query.js.map +1 -1
- package/dist-server/service/data-set/data-set-mutation.js +40 -14
- package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
- package/dist-server/service/data-set/data-set-query.js +190 -3
- package/dist-server/service/data-set/data-set-query.js.map +1 -1
- package/dist-server/service/data-set/data-set-type.js +84 -3
- package/dist-server/service/data-set/data-set-type.js.map +1 -1
- package/dist-server/service/data-set/data-set.js +110 -15
- package/dist-server/service/data-set/data-set.js.map +1 -1
- package/dist-server/service/index.js +6 -2
- package/dist-server/service/index.js.map +1 -1
- package/package.json +19 -13
- package/server/controllers/create-data-sample.ts +177 -0
- package/server/controllers/data-use-case.ts +85 -0
- package/server/controllers/index.ts +2 -0
- package/server/controllers/jasper-report.ts +170 -0
- package/server/index.ts +1 -0
- package/server/routes.ts +21 -31
- package/server/service/data-item/data-item-mutation.ts +6 -1
- package/server/service/data-item/data-item-query.ts +8 -3
- package/server/service/data-item/data-item-type.ts +10 -6
- package/server/service/data-item/data-item.ts +15 -4
- package/server/service/data-ooc/data-ooc-mutation.ts +150 -0
- package/server/service/data-ooc/data-ooc-query.ts +69 -0
- package/server/service/data-ooc/data-ooc-subscription.ts +51 -0
- package/server/service/data-ooc/data-ooc-type.ts +68 -0
- package/server/service/data-ooc/data-ooc.ts +204 -0
- package/server/service/data-ooc/index.ts +7 -0
- package/server/service/data-sample/data-sample-mutation.ts +3 -168
- package/server/service/data-sample/data-sample-query.ts +9 -3
- package/server/service/data-sample/data-sample-type.ts +7 -28
- package/server/service/data-sample/data-sample.ts +33 -3
- package/server/service/data-sensor/data-sensor-query.ts +9 -3
- package/server/service/data-set/data-set-mutation.ts +53 -14
- package/server/service/data-set/data-set-query.ts +161 -4
- package/server/service/data-set/data-set-type.ts +65 -4
- package/server/service/data-set/data-set.ts +100 -12
- package/server/service/index.ts +6 -2
- package/things-factory.config.js +35 -7
- package/translations/en.json +46 -3
- package/translations/ko.json +45 -3
- package/translations/ms.json +44 -3
- package/translations/zh.json +44 -3
- package/client/pages/data-sample.js +0 -307
- package/client/pages/data-set.js +0 -457
@@ -0,0 +1,170 @@
|
|
1
|
+
import FormData from 'form-data'
|
2
|
+
import fetch from 'node-fetch'
|
3
|
+
|
4
|
+
import { STORAGE } from '@things-factory/attachment-base'
|
5
|
+
import { AthenaController } from '@things-factory/aws-base'
|
6
|
+
import { config } from '@things-factory/env'
|
7
|
+
|
8
|
+
const dataReportConfig = config.get('dataReport')
|
9
|
+
const { jasper: {
|
10
|
+
endpoint: { protocol: PROTOCOL, host:HOST, port:PORT },
|
11
|
+
datasource: { database: DATABASE } }
|
12
|
+
} = dataReportConfig
|
13
|
+
|
14
|
+
/** author: ywnam123 */
|
15
|
+
function transformValuesToRows(queryResult) {
|
16
|
+
var parseData = []
|
17
|
+
let index = 1
|
18
|
+
for (let i = 0; i < queryResult.Items.length; i++) {
|
19
|
+
var j = 0
|
20
|
+
const data = JSON.parse(queryResult.Items[i].data)
|
21
|
+
const spec = JSON.parse(queryResult.Items[i].spec)
|
22
|
+
|
23
|
+
for (let key in data) {
|
24
|
+
if (Array.isArray(data[key])) {
|
25
|
+
for (j = 0; j < data[key].length; j++) {
|
26
|
+
for (let specKey in spec) {
|
27
|
+
if (key === specKey) {
|
28
|
+
parseData.push({
|
29
|
+
item: spec[specKey].name,
|
30
|
+
index: index + j,
|
31
|
+
value: String(data[key][j])
|
32
|
+
})
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
} else {
|
37
|
+
parseData.push({
|
38
|
+
item: key,
|
39
|
+
index,
|
40
|
+
value: String(data[key])
|
41
|
+
})
|
42
|
+
}
|
43
|
+
}
|
44
|
+
if (j !== 0) {
|
45
|
+
index = index + j
|
46
|
+
} else {
|
47
|
+
index = index + 1
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
/** @todo considering trasformation in lambda, as massive dataset */
|
53
|
+
function pivotData(rows) {
|
54
|
+
let parsedData = []
|
55
|
+
let index = 1
|
56
|
+
for (let i = 0; i < rows.length; i++) {
|
57
|
+
let j = 0
|
58
|
+
const data = JSON.parse(rows[i].data)
|
59
|
+
const spec = JSON.parse(rows[i].spec)
|
60
|
+
|
61
|
+
for (let key in data) {
|
62
|
+
/** @todo rule to display or not, about unspecified spec */
|
63
|
+
const value = data[key]
|
64
|
+
!(spec[key]?.hidden) && parsedData.push({
|
65
|
+
item: spec[key]?.name || key,
|
66
|
+
index,
|
67
|
+
value: Array.isArray(value) ? value.join(', ') : value
|
68
|
+
})
|
69
|
+
}
|
70
|
+
if (j !== 0) {
|
71
|
+
index = index + j
|
72
|
+
} else {
|
73
|
+
index = index + 1
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
return parsedData
|
78
|
+
}
|
79
|
+
|
80
|
+
function parseJsonDataField(rows) {
|
81
|
+
let parsedData = []
|
82
|
+
for (let i = 0; i < rows.length; i++) {
|
83
|
+
const row = rows[i]
|
84
|
+
const data = JSON.parse(row.data)
|
85
|
+
for (let key in data) {
|
86
|
+
if (Array.isArray(data[key])) {
|
87
|
+
data[key] = data[key].toString()
|
88
|
+
}
|
89
|
+
}
|
90
|
+
delete row.data
|
91
|
+
parsedData.push({ ...row, ...data })
|
92
|
+
}
|
93
|
+
|
94
|
+
return parsedData
|
95
|
+
}
|
96
|
+
|
97
|
+
const athenaClient = new AthenaController()
|
98
|
+
async function queryAthena(params) {
|
99
|
+
const { table, domain, dataSetId, fromWorkDate, toWorkDate, workShift, timezone } = params
|
100
|
+
const queryData = {
|
101
|
+
sql: `SELECT name, description, data, spec, workdate, workshift,
|
102
|
+
DATE_FORMAT(
|
103
|
+
FROM_UNIXTIME(collected_at / 1000 / 1000) AT TIME ZONE '${timezone || 'UTC'}',
|
104
|
+
'%Y-%m-%d %H:%i:%s'
|
105
|
+
) AS collected_at
|
106
|
+
FROM ${table}
|
107
|
+
WHERE domain='${domain}'
|
108
|
+
AND datasetid = '${dataSetId}'
|
109
|
+
AND workdate >= '${fromWorkDate}'
|
110
|
+
AND workdate <= '${toWorkDate}'
|
111
|
+
${workShift ? 'AND workshift = \'' + workShift + '\'' : ''}
|
112
|
+
ORDER BY collected_at`,
|
113
|
+
db: DATABASE
|
114
|
+
}
|
115
|
+
// and json_extract_scalar(data, '$.dauid') = 'A8032AD81730'
|
116
|
+
|
117
|
+
return await athenaClient.query(queryData)
|
118
|
+
}
|
119
|
+
export async function renderJasperReport(context: any) {
|
120
|
+
const { state: { domain }, query } = context
|
121
|
+
|
122
|
+
const template = await STORAGE.readFile(query['template'] || 'dynamic_header_sample.jrxml', 'utf-8')
|
123
|
+
let templateType = query['templateType'] || 'crosstab'
|
124
|
+
let parsedData = []
|
125
|
+
|
126
|
+
// @todo: get dataset timezone
|
127
|
+
/**
|
128
|
+
* const variables = await gql(dataSet(id:${dataSetId}) {
|
129
|
+
* name, description, partition_keys, timezone
|
130
|
+
* })
|
131
|
+
*/
|
132
|
+
|
133
|
+
query['domain'] = domain?.subdomain
|
134
|
+
query['timezone'] = domain?.timezone
|
135
|
+
const queryResult = await queryAthena(query)
|
136
|
+
const rows = queryResult.Items
|
137
|
+
|
138
|
+
if (!rows.length) {
|
139
|
+
return '<h3>Not found result.</h3>'
|
140
|
+
}
|
141
|
+
else {
|
142
|
+
const firstRow = rows[0]
|
143
|
+
// uses the first row values as data-set has no history data.
|
144
|
+
const parameters = {
|
145
|
+
name: firstRow.name,
|
146
|
+
description: firstRow.description,
|
147
|
+
...query
|
148
|
+
}
|
149
|
+
|
150
|
+
if (templateType === 'crosstab') {
|
151
|
+
parsedData = pivotData(rows)
|
152
|
+
} else {
|
153
|
+
parsedData = parseJsonDataField(rows)
|
154
|
+
}
|
155
|
+
|
156
|
+
const formData = new FormData()
|
157
|
+
formData.append('template', template)
|
158
|
+
formData.append('jsonString', JSON.stringify(parsedData))
|
159
|
+
formData.append('parameters', JSON.stringify(parameters))
|
160
|
+
|
161
|
+
const reportUrl = `${PROTOCOL || 'http'}://${HOST}:${PORT}/rest/report/show_html`
|
162
|
+
const response = await fetch(reportUrl, {
|
163
|
+
method: 'POST',
|
164
|
+
body: formData
|
165
|
+
})
|
166
|
+
|
167
|
+
return await response.text()
|
168
|
+
}
|
169
|
+
|
170
|
+
}
|
package/server/index.ts
CHANGED
package/server/routes.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
import { getConnection } from 'typeorm'
|
2
2
|
|
3
|
-
import {
|
4
|
-
|
3
|
+
import { User } from '@things-factory/auth-base'
|
4
|
+
|
5
|
+
import { createDataSample } from './controllers/create-data-sample'
|
6
|
+
import { renderJasperReport } from './controllers/jasper-report'
|
5
7
|
import { DataSensor } from './service/data-sensor/data-sensor'
|
6
8
|
|
7
9
|
const debug = require('debug')('things-factory:dataset:routes')
|
@@ -47,41 +49,26 @@ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRout
|
|
47
49
|
|
48
50
|
const domain = sensor.domain
|
49
51
|
const dataSet = sensor.dataSet
|
50
|
-
const user =
|
51
|
-
|
52
|
-
const dataItems = await tx.getRepository(DataItem).find({
|
52
|
+
const user: User = await tx.getRepository(User).findOne({
|
53
53
|
where: {
|
54
|
-
|
55
|
-
|
56
|
-
},
|
57
|
-
order: {
|
58
|
-
sequence: 'DESC'
|
54
|
+
reference: sensor.appliance.id,
|
55
|
+
userType: 'appliance'
|
59
56
|
}
|
60
57
|
})
|
61
58
|
|
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
|
-
})
|
59
|
+
return await createDataSample(
|
60
|
+
{
|
61
|
+
dataSet,
|
62
|
+
data,
|
63
|
+
rawData,
|
64
|
+
source: deviceId,
|
65
|
+
collectedAt: new Date(timestamp)
|
66
|
+
},
|
67
|
+
{ state: { domain, user, tx } }
|
68
|
+
)
|
82
69
|
})
|
83
70
|
|
84
|
-
|
71
|
+
context.status = 200
|
85
72
|
})
|
86
73
|
})
|
87
74
|
|
@@ -101,4 +88,7 @@ process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRo
|
|
101
88
|
/*
|
102
89
|
* can add domain private routes to application (auth required, tenancy required)
|
103
90
|
*/
|
91
|
+
domainPrivateRouter.get('/data-report/jasper', async (context, next) => {
|
92
|
+
context.body = await renderJasperReport(context)
|
93
|
+
})
|
104
94
|
})
|
@@ -24,9 +24,14 @@ export class DataItemMutation {
|
|
24
24
|
await dataItemRepo.delete({ domain, dataSet: dataSetId })
|
25
25
|
|
26
26
|
for (let i = 0; i < patches.length; i++) {
|
27
|
+
const patch = patches[i]
|
28
|
+
if (!patch.options) {
|
29
|
+
patch.options = {}
|
30
|
+
}
|
31
|
+
|
27
32
|
const result = await dataItemRepo.save({
|
28
33
|
quota: 1,
|
29
|
-
...
|
34
|
+
...patch,
|
30
35
|
sequence: i,
|
31
36
|
dataSet,
|
32
37
|
domain,
|
@@ -2,7 +2,7 @@ import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from
|
|
2
2
|
import { getRepository } from 'typeorm'
|
3
3
|
|
4
4
|
import { User } from '@things-factory/auth-base'
|
5
|
-
import {
|
5
|
+
import { Domain, getQueryBuilderFromListParams, ListParam } from '@things-factory/shell'
|
6
6
|
|
7
7
|
import { DataSet } from '../data-set/data-set'
|
8
8
|
import { DataItem } from './data-item'
|
@@ -25,8 +25,13 @@ export class DataItemQuery {
|
|
25
25
|
async dataItems(@Args() params: ListParam, @Ctx() context: any): Promise<DataItemList> {
|
26
26
|
const { domain } = context.state
|
27
27
|
|
28
|
-
const
|
29
|
-
|
28
|
+
const queryBuilder = getQueryBuilderFromListParams({
|
29
|
+
repository: getRepository(DataItem),
|
30
|
+
params,
|
31
|
+
domain
|
32
|
+
})
|
33
|
+
|
34
|
+
const [items, total] = await queryBuilder.getManyAndCount()
|
30
35
|
|
31
36
|
return { items, total }
|
32
37
|
}
|
@@ -1,16 +1,14 @@
|
|
1
|
-
import { Field, ID, InputType, Int, ObjectType } from 'type-graphql'
|
2
|
-
|
3
|
-
import { ScalarObject } from '@things-factory/shell'
|
4
|
-
|
5
1
|
import { DataItem, DataItemType } from './data-item'
|
2
|
+
import { Field, ID, InputType, Int, ObjectType } from 'type-graphql'
|
3
|
+
import { ObjectRef, ScalarObject } from '@things-factory/shell'
|
6
4
|
|
7
5
|
@InputType()
|
8
6
|
export class DataItemPatch {
|
9
7
|
@Field(type => ID, { nullable: true })
|
10
8
|
id?: string
|
11
9
|
|
12
|
-
@Field({ nullable: true })
|
13
|
-
|
10
|
+
@Field(type => ObjectRef, { nullable: true })
|
11
|
+
dataSet?: ObjectRef
|
14
12
|
|
15
13
|
@Field({ nullable: true })
|
16
14
|
name?: string
|
@@ -30,12 +28,18 @@ export class DataItemPatch {
|
|
30
28
|
@Field(type => ScalarObject, { nullable: true })
|
31
29
|
options?: ScalarObject
|
32
30
|
|
31
|
+
@Field({ nullable: true })
|
32
|
+
unit?: string
|
33
|
+
|
33
34
|
@Field(type => Int, { nullable: true })
|
34
35
|
quota?: number
|
35
36
|
|
36
37
|
@Field({ nullable: true })
|
37
38
|
active?: boolean
|
38
39
|
|
40
|
+
@Field({ nullable: true })
|
41
|
+
hidden?: boolean
|
42
|
+
|
39
43
|
@Field(type => ScalarObject, { nullable: true })
|
40
44
|
spec?: ScalarObject
|
41
45
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'
|
2
1
|
import {
|
3
2
|
Column,
|
4
3
|
CreateDateColumn,
|
@@ -9,11 +8,11 @@ import {
|
|
9
8
|
RelationId,
|
10
9
|
UpdateDateColumn
|
11
10
|
} from 'typeorm'
|
12
|
-
|
13
|
-
import { User } from '@things-factory/auth-base'
|
14
11
|
import { Domain, ScalarObject } from '@things-factory/shell'
|
12
|
+
import { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'
|
15
13
|
|
16
14
|
import { DataSet } from '../data-set/data-set'
|
15
|
+
import { User } from '@things-factory/auth-base'
|
17
16
|
|
18
17
|
export enum DataItemType {
|
19
18
|
number = 'number',
|
@@ -54,7 +53,7 @@ export class DataItem {
|
|
54
53
|
description?: string
|
55
54
|
|
56
55
|
@ManyToOne(type => DataSet, dataSet => dataSet.dataItems, { onDelete: 'CASCADE' })
|
57
|
-
@Field(type => DataSet, { nullable:
|
56
|
+
@Field(type => DataSet, { nullable: false })
|
58
57
|
dataSet?: DataSet
|
59
58
|
|
60
59
|
@RelationId((dataItem: DataItem) => dataItem.dataSet)
|
@@ -76,6 +75,12 @@ export class DataItem {
|
|
76
75
|
@Field({ nullable: true })
|
77
76
|
active?: boolean
|
78
77
|
|
78
|
+
@Column({
|
79
|
+
nullable: true
|
80
|
+
})
|
81
|
+
@Field({ nullable: true })
|
82
|
+
hidden?: boolean
|
83
|
+
|
79
84
|
@Column({
|
80
85
|
nullable: true
|
81
86
|
})
|
@@ -86,6 +91,12 @@ export class DataItem {
|
|
86
91
|
@Field(type => ScalarObject, { nullable: true })
|
87
92
|
options?: ScalarObject
|
88
93
|
|
94
|
+
@Column({
|
95
|
+
nullable: true
|
96
|
+
})
|
97
|
+
@Field({ nullable: true })
|
98
|
+
unit?: string
|
99
|
+
|
89
100
|
@Column({
|
90
101
|
nullable: true
|
91
102
|
})
|
@@ -0,0 +1,150 @@
|
|
1
|
+
import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
|
2
|
+
|
3
|
+
import { DataOoc, DataOocStatus } from './data-ooc'
|
4
|
+
import { DataOocPatch, NewDataOoc } from './data-ooc-type'
|
5
|
+
|
6
|
+
@Resolver(DataOoc)
|
7
|
+
export class DataOocMutation {
|
8
|
+
@Directive('@privilege(category: "data-ooc", privilege: "mutation", domainOwnerGranted: true)')
|
9
|
+
@Directive('@transaction')
|
10
|
+
@Mutation(returns => DataOoc, { description: 'To create new DataOoc' })
|
11
|
+
async createDataOoc(@Arg('dataOoc') dataOoc: NewDataOoc, @Ctx() context: any): Promise<DataOoc> {
|
12
|
+
const { domain, user, tx } = context.state
|
13
|
+
|
14
|
+
const state = dataOoc.state || DataOocStatus.CREATED
|
15
|
+
const history = [
|
16
|
+
{
|
17
|
+
user: {
|
18
|
+
id: user.id,
|
19
|
+
name: user.name
|
20
|
+
},
|
21
|
+
state,
|
22
|
+
timestamp: Date.now()
|
23
|
+
}
|
24
|
+
]
|
25
|
+
|
26
|
+
return await tx.getRepository(DataOoc).save({
|
27
|
+
...dataOoc,
|
28
|
+
state,
|
29
|
+
domain,
|
30
|
+
history,
|
31
|
+
creator: user,
|
32
|
+
updater: user
|
33
|
+
})
|
34
|
+
}
|
35
|
+
|
36
|
+
@Directive('@privilege(category: "data-ooc", privilege: "mutation", domainOwnerGranted: true)')
|
37
|
+
@Directive('@transaction')
|
38
|
+
@Mutation(returns => DataOoc, { description: 'To modify DataOoc information' })
|
39
|
+
async updateDataOoc(@Arg('id') id: string, @Arg('patch') patch: DataOocPatch, @Ctx() context: any): Promise<DataOoc> {
|
40
|
+
const { domain, user, tx } = context.state
|
41
|
+
|
42
|
+
const repository = tx.getRepository(DataOoc)
|
43
|
+
const dataOoc = await repository.findOne({
|
44
|
+
where: { domain, id }
|
45
|
+
})
|
46
|
+
|
47
|
+
const state = patch.state || dataOoc.state
|
48
|
+
|
49
|
+
const history = dataOoc.history || []
|
50
|
+
history.push({
|
51
|
+
user: {
|
52
|
+
id: user.id,
|
53
|
+
name: user.name
|
54
|
+
},
|
55
|
+
state,
|
56
|
+
comment: patch.correctiveAction || '',
|
57
|
+
timestamp: Date.now()
|
58
|
+
})
|
59
|
+
|
60
|
+
const more = {} as any
|
61
|
+
|
62
|
+
if (dataOoc.state !== patch.state) {
|
63
|
+
if (patch.state === DataOocStatus.CORRECTED) {
|
64
|
+
more.corrector = user
|
65
|
+
} else {
|
66
|
+
more.corrector = null
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
return await repository.save({
|
71
|
+
...dataOoc,
|
72
|
+
...patch,
|
73
|
+
...more,
|
74
|
+
state,
|
75
|
+
history,
|
76
|
+
updater: user
|
77
|
+
})
|
78
|
+
}
|
79
|
+
|
80
|
+
// @Directive('@privilege(category: "data-ooc", privilege: "mutation", domainOwnerGranted: true)')
|
81
|
+
// @Directive('@transaction')
|
82
|
+
// @Mutation(returns => [DataOoc], { description: "To modify multiple DataOoc' information" })
|
83
|
+
// async updateMultipleDataOoc(
|
84
|
+
// @Arg('patches', type => [DataOocPatch]) patches: DataOocPatch[],
|
85
|
+
// @Ctx() context: any
|
86
|
+
// ): Promise<DataOoc[]> {
|
87
|
+
// const { domain, user, tx } = context.state
|
88
|
+
|
89
|
+
// let results = []
|
90
|
+
// const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
|
91
|
+
// const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
|
92
|
+
// const dataOocRepo = tx.getRepository(DataOoc)
|
93
|
+
|
94
|
+
// if (_createRecords.length > 0) {
|
95
|
+
// for (let i = 0; i < _createRecords.length; i++) {
|
96
|
+
// const newRecord = _createRecords[i]
|
97
|
+
|
98
|
+
// const result = await dataOocRepo.save({
|
99
|
+
// ...newRecord,
|
100
|
+
// domain,
|
101
|
+
// creator: user,
|
102
|
+
// updater: user
|
103
|
+
// })
|
104
|
+
|
105
|
+
// results.push({ ...result, cuFlag: '+' })
|
106
|
+
// }
|
107
|
+
// }
|
108
|
+
|
109
|
+
// if (_updateRecords.length > 0) {
|
110
|
+
// for (let i = 0; i < _updateRecords.length; i++) {
|
111
|
+
// const newRecord = _updateRecords[i]
|
112
|
+
// const dataOoc = await dataOocRepo.findOne(newRecord.id)
|
113
|
+
|
114
|
+
// const result = await dataOocRepo.save({
|
115
|
+
// ...dataOoc,
|
116
|
+
// ...newRecord,
|
117
|
+
// updater: user
|
118
|
+
// })
|
119
|
+
|
120
|
+
// results.push({ ...result, cuFlag: 'M' })
|
121
|
+
// }
|
122
|
+
// }
|
123
|
+
|
124
|
+
// return results
|
125
|
+
// }
|
126
|
+
|
127
|
+
// @Directive('@privilege(category: "data-ooc", privilege: "mutation", domainOwnerGranted: true)')
|
128
|
+
// @Directive('@transaction')
|
129
|
+
// @Mutation(returns => Boolean, { description: 'To delete DataOoc' })
|
130
|
+
// async deleteDataOoc(@Arg('id') id: string, @Ctx() context: any): Promise<boolean> {
|
131
|
+
// const { domain, tx } = context.state
|
132
|
+
|
133
|
+
// await tx.getRepository(DataOoc).delete({ domain, id })
|
134
|
+
// return true
|
135
|
+
// }
|
136
|
+
|
137
|
+
// @Directive('@privilege(category: "data-ooc", privilege: "mutation", domainOwnerGranted: true)')
|
138
|
+
// @Directive('@transaction')
|
139
|
+
// @Mutation(returns => Boolean, { description: 'To delete multiple dataOocs' })
|
140
|
+
// async deleteDataOocs(@Arg('ids', type => [String]) ids: string[], @Ctx() context: any): Promise<boolean> {
|
141
|
+
// const { domain, tx } = context.state
|
142
|
+
|
143
|
+
// await tx.getRepository(DataOoc).delete({
|
144
|
+
// domain,
|
145
|
+
// id: In(ids)
|
146
|
+
// })
|
147
|
+
|
148
|
+
// return true
|
149
|
+
// }
|
150
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
|
2
|
+
import { getRepository } from 'typeorm'
|
3
|
+
|
4
|
+
import { User } from '@things-factory/auth-base'
|
5
|
+
import { Domain, getQueryBuilderFromListParams, ListParam } from '@things-factory/shell'
|
6
|
+
|
7
|
+
import { DataSample } from '../data-sample/data-sample'
|
8
|
+
import { DataSet } from '../data-set/data-set'
|
9
|
+
import { DataOoc } from './data-ooc'
|
10
|
+
import { DataOocList } from './data-ooc-type'
|
11
|
+
|
12
|
+
@Resolver(DataOoc)
|
13
|
+
export class DataOocQuery {
|
14
|
+
@Directive('@privilege(category: "data-ooc", privilege: "query", domainOwnerGranted: true)')
|
15
|
+
@Query(returns => DataOoc, { description: 'To fetch a DataOoc' })
|
16
|
+
async dataOoc(@Arg('id') id: string, @Ctx() context: any): Promise<DataOoc> {
|
17
|
+
const { domain } = context.state
|
18
|
+
|
19
|
+
return await getRepository(DataOoc).findOne({
|
20
|
+
where: { domain, id }
|
21
|
+
})
|
22
|
+
}
|
23
|
+
|
24
|
+
@Directive('@privilege(category: "data-ooc", privilege: "query", domainOwnerGranted: true)')
|
25
|
+
@Query(returns => DataOocList, { description: 'To fetch multiple DataOoc' })
|
26
|
+
async dataOocs(@Args() params: ListParam, @Ctx() context: any): Promise<DataOocList> {
|
27
|
+
const { domain } = context.state
|
28
|
+
|
29
|
+
const queryBuilder = getQueryBuilderFromListParams({
|
30
|
+
repository: getRepository(DataOoc),
|
31
|
+
params,
|
32
|
+
domain,
|
33
|
+
searchables: ['name', 'description']
|
34
|
+
})
|
35
|
+
|
36
|
+
const [items, total] = await queryBuilder.getManyAndCount()
|
37
|
+
|
38
|
+
return { items, total }
|
39
|
+
}
|
40
|
+
|
41
|
+
@FieldResolver(type => DataSet)
|
42
|
+
async dataSet(@Root() dataOoc: DataOoc): Promise<DataSet> {
|
43
|
+
return await getRepository(DataSet).findOne({
|
44
|
+
id: dataOoc.dataSetId
|
45
|
+
})
|
46
|
+
}
|
47
|
+
|
48
|
+
@FieldResolver(type => DataSample)
|
49
|
+
async dataSample(@Root() dataOoc: DataOoc): Promise<DataSample> {
|
50
|
+
return await getRepository(DataSample).findOne({
|
51
|
+
id: dataOoc.dataSampleId
|
52
|
+
})
|
53
|
+
}
|
54
|
+
|
55
|
+
@FieldResolver(type => Domain)
|
56
|
+
async domain(@Root() dataOoc: DataOoc): Promise<Domain> {
|
57
|
+
return await getRepository(Domain).findOne(dataOoc.domainId)
|
58
|
+
}
|
59
|
+
|
60
|
+
@FieldResolver(type => User)
|
61
|
+
async updater(@Root() dataOoc: DataOoc): Promise<User> {
|
62
|
+
return await getRepository(User).findOne(dataOoc.updaterId)
|
63
|
+
}
|
64
|
+
|
65
|
+
@FieldResolver(type => User)
|
66
|
+
async creator(@Root() dataOoc: DataOoc): Promise<User> {
|
67
|
+
return await getRepository(User).findOne(dataOoc.creatorId)
|
68
|
+
}
|
69
|
+
}
|
@@ -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
|
+
}
|