@things-factory/dataset 8.0.0-beta.8 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. package/client/activities/activity-data-collect-edit.ts +105 -0
  2. package/client/activities/activity-data-collect-view.ts +91 -0
  3. package/client/activities/activity-data-review-edit.ts +133 -0
  4. package/client/activities/activity-data-review-view.ts +145 -0
  5. package/client/activities/activity-ooc-resolve-edit.ts +195 -0
  6. package/client/activities/activity-ooc-resolve-view.ts +143 -0
  7. package/client/activities/activity-ooc-review-edit.ts +173 -0
  8. package/client/activities/activity-ooc-review-view.ts +129 -0
  9. package/client/bootstrap.ts +35 -0
  10. package/client/components/data-entry-form.ts +109 -0
  11. package/client/index.ts +1 -0
  12. package/client/pages/data-archive/data-archive-list-page.ts +277 -0
  13. package/client/pages/data-archive/data-archive-request-popup.ts +177 -0
  14. package/client/pages/data-entry/data-entry-list-page.ts +464 -0
  15. package/client/pages/data-key-set/data-key-item-list.ts +183 -0
  16. package/client/pages/data-key-set/data-key-set-importer.ts +89 -0
  17. package/client/pages/data-key-set/data-key-set-list-page.ts +413 -0
  18. package/client/pages/data-ooc/data-ooc-list-page.ts +549 -0
  19. package/client/pages/data-ooc/data-ooc-page.ts +164 -0
  20. package/client/pages/data-ooc/data-ooc-view.ts +236 -0
  21. package/client/pages/data-ooc/data-oocs-page.ts +200 -0
  22. package/client/pages/data-report/data-report-embed-page.ts +108 -0
  23. package/client/pages/data-report/data-report-list-page.ts +454 -0
  24. package/client/pages/data-report/data-report-samples-page.ts +174 -0
  25. package/client/pages/data-report/jasper-report-oocs-page.ts +110 -0
  26. package/client/pages/data-report/jasper-report-samples-crosstab-page.ts +110 -0
  27. package/client/pages/data-report/jasper-report-samples-page.ts +110 -0
  28. package/client/pages/data-sample/data-sample-list-page.ts +442 -0
  29. package/client/pages/data-sample/data-sample-page.ts +55 -0
  30. package/client/pages/data-sample/data-sample-search-page.ts +424 -0
  31. package/client/pages/data-sample/data-sample-view.ts +292 -0
  32. package/client/pages/data-sample/data-samples-page.ts +249 -0
  33. package/client/pages/data-sensor/data-sensor-list-page.ts +456 -0
  34. package/client/pages/data-set/data-item-list.ts +304 -0
  35. package/client/pages/data-set/data-set-importer.ts +89 -0
  36. package/client/pages/data-set/data-set-list-page.ts +1078 -0
  37. package/client/pages/data-summary/data-summary-list-page.ts +363 -0
  38. package/client/pages/data-summary/data-summary-period-page.ts +439 -0
  39. package/client/pages/data-summary/data-summary-search-page.ts +426 -0
  40. package/client/pages/data-summary/data-summary-view.ts +133 -0
  41. package/client/route.ts +91 -0
  42. package/client/tsconfig.json +13 -0
  43. package/dist-client/pages/data-entry/data-entry-list-page.js +2 -2
  44. package/dist-client/pages/data-entry/data-entry-list-page.js.map +1 -1
  45. package/dist-client/tsconfig.tsbuildinfo +1 -1
  46. package/dist-server/service/data-sample/data-sample-query.d.ts +1 -1
  47. package/dist-server/service/data-sample/data-sample-query.js +3 -3
  48. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  49. package/dist-server/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +26 -26
  51. package/server/activities/activity-data-collect.ts +100 -0
  52. package/server/activities/activity-data-review.ts +82 -0
  53. package/server/activities/activity-ooc-resolve.ts +123 -0
  54. package/server/activities/activity-ooc-review.ts +144 -0
  55. package/server/activities/index.ts +11 -0
  56. package/server/controllers/create-data-sample.ts +426 -0
  57. package/server/controllers/data-use-case.ts +98 -0
  58. package/server/controllers/finalize-data-collection.ts +388 -0
  59. package/server/controllers/index.ts +3 -0
  60. package/server/controllers/issue-data-collection-task.ts +70 -0
  61. package/server/controllers/jasper-report.ts +186 -0
  62. package/server/controllers/query-data-summary-by-period.ts +178 -0
  63. package/server/controllers/shiny-report.ts +54 -0
  64. package/server/engine/index.ts +1 -0
  65. package/server/engine/task/create-data-sample.ts +100 -0
  66. package/server/engine/task/index.ts +2 -0
  67. package/server/engine/task/issue-collect-data.ts +45 -0
  68. package/server/index.ts +8 -0
  69. package/server/routes.ts +188 -0
  70. package/server/service/data-archive/data-archive-mutation.ts +273 -0
  71. package/server/service/data-archive/data-archive-query.ts +58 -0
  72. package/server/service/data-archive/data-archive-type.ts +48 -0
  73. package/server/service/data-archive/data-archive.ts +69 -0
  74. package/server/service/data-archive/index.ts +6 -0
  75. package/server/service/data-key-set/data-key-item-type.ts +31 -0
  76. package/server/service/data-key-set/data-key-set-mutation.ts +201 -0
  77. package/server/service/data-key-set/data-key-set-query.ts +68 -0
  78. package/server/service/data-key-set/data-key-set-type.ts +70 -0
  79. package/server/service/data-key-set/data-key-set.ts +86 -0
  80. package/server/service/data-key-set/index.ts +6 -0
  81. package/server/service/data-ooc/data-ooc-mutation.ts +154 -0
  82. package/server/service/data-ooc/data-ooc-query.ts +106 -0
  83. package/server/service/data-ooc/data-ooc-subscription.ts +48 -0
  84. package/server/service/data-ooc/data-ooc-type.ts +71 -0
  85. package/server/service/data-ooc/data-ooc.ts +259 -0
  86. package/server/service/data-ooc/index.ts +7 -0
  87. package/server/service/data-sample/data-sample-mutation.ts +18 -0
  88. package/server/service/data-sample/data-sample-query.ts +215 -0
  89. package/server/service/data-sample/data-sample-type.ts +47 -0
  90. package/server/service/data-sample/data-sample.ts +193 -0
  91. package/server/service/data-sample/index.ts +6 -0
  92. package/server/service/data-sensor/data-sensor-mutation.ts +116 -0
  93. package/server/service/data-sensor/data-sensor-query.ts +76 -0
  94. package/server/service/data-sensor/data-sensor-type.ts +104 -0
  95. package/server/service/data-sensor/data-sensor.ts +126 -0
  96. package/server/service/data-sensor/index.ts +6 -0
  97. package/server/service/data-set/data-item-type.ts +155 -0
  98. package/server/service/data-set/data-set-mutation.ts +552 -0
  99. package/server/service/data-set/data-set-query.ts +461 -0
  100. package/server/service/data-set/data-set-type.ts +204 -0
  101. package/server/service/data-set/data-set.ts +326 -0
  102. package/server/service/data-set/index.ts +6 -0
  103. package/server/service/data-set-history/data-set-history-query.ts +126 -0
  104. package/server/service/data-set-history/data-set-history-type.ts +12 -0
  105. package/server/service/data-set-history/data-set-history.ts +217 -0
  106. package/server/service/data-set-history/event-subscriber.ts +17 -0
  107. package/server/service/data-set-history/index.ts +7 -0
  108. package/server/service/data-spec/data-spec-manager.ts +21 -0
  109. package/server/service/data-spec/data-spec-query.ts +21 -0
  110. package/server/service/data-spec/data-spec.ts +45 -0
  111. package/server/service/data-spec/index.ts +5 -0
  112. package/server/service/data-summary/data-summary-mutation.ts +45 -0
  113. package/server/service/data-summary/data-summary-query.ts +179 -0
  114. package/server/service/data-summary/data-summary-type.ts +86 -0
  115. package/server/service/data-summary/data-summary.ts +170 -0
  116. package/server/service/data-summary/index.ts +7 -0
  117. package/server/service/index.ts +57 -0
  118. package/server/tsconfig.json +10 -0
  119. package/server/utils/config-resolver.ts +29 -0
  120. package/server/utils/index.ts +1 -0
@@ -0,0 +1,178 @@
1
+ import { In } from 'typeorm'
2
+
3
+ import { getQueryBuilderFromListParams, getRepository, getTimesForPeriod, ListParam, Sorting } from '@things-factory/shell'
4
+
5
+ import { DataSet, DataSetSummaryPeriodType } from '../service/data-set/data-set'
6
+ import { DataSummary } from '../service/data-summary/data-summary'
7
+
8
+ const STAT_FUNCTION_MAP = {
9
+ sum: 'sum',
10
+ mean: 'mean',
11
+ stddev: 'standardDeviation',
12
+ variance: 'variance',
13
+ min: 'min',
14
+ max: 'max',
15
+ range: 'range',
16
+ median: 'median',
17
+ mode: 'mode'
18
+ }
19
+
20
+ export async function queryDataSummaryByPeriod(
21
+ period: 'today' | 'this month' | '30 days' | 'this year' | '12 months',
22
+ dataSetName: string,
23
+ dataKeys: string[] | null,
24
+ params: ListParam,
25
+ context: ResolverContext
26
+ ): Promise<DataSummary[]> {
27
+ const { domain, user, tx } = context.state
28
+ const { t } = context
29
+
30
+ const dataSet = await getRepository(DataSet).findOne({
31
+ where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, name: dataSetName },
32
+ relations: ['dataKeySet']
33
+ })
34
+
35
+ if (!dataSet) {
36
+ throw new Error(t('error.dataset not found', { dataSetName }))
37
+ }
38
+
39
+ // limitations
40
+ const summaryPeriodType = dataSet.summaryPeriod
41
+ if ((summaryPeriodType == DataSetSummaryPeriodType.Day || summaryPeriodType == DataSetSummaryPeriodType.WorkDate) && period == 'today') {
42
+ throw new Error(t('error.summary not supported', { dataSetName, period: t(`label.period-${period}`) }))
43
+ }
44
+
45
+ // dataKeys 가 설정되지 않았다면, dataSet의 dataKeySet을 그대로 적용한다는 의미임.
46
+ // dataKeys == [] 라면, dataKeySet을 적용하지 않는다는 의미임.
47
+ // dataKeys에는 dataKeySet의 dataKey 값을 따라야 한다.
48
+ const dataKeyItems = !dataSet.dataKeySet?.dataKeyItems
49
+ ? []
50
+ : dataKeys
51
+ ? dataKeys
52
+ .map(dataKey => {
53
+ return dataSet.dataKeySet.dataKeyItems.find(item => item.dataKey == dataKey)
54
+ })
55
+ .filter(Boolean)
56
+ : dataSet.dataKeySet.dataKeyItems
57
+
58
+ const searchables = dataKeyItems.map((item, index) => `key0${index + 1}`)
59
+ const dataItems = dataSet.dataItems.filter(item => item.agg)
60
+
61
+ const { from, to } = await getTimesForPeriod(period, context)
62
+
63
+ const selectPeriod = queryBuilder => {
64
+ if (period == 'today') {
65
+ queryBuilder.addSelect('summary.date', 'date').addSelect('summary.period', 'period')
66
+ } else if (['this year', '12 months'].includes(period)) {
67
+ queryBuilder.addSelect('SUBSTRING(summary.date, 1, 7) AS month')
68
+ } else {
69
+ queryBuilder.addSelect('summary.date', 'date')
70
+ }
71
+
72
+ return queryBuilder
73
+ }
74
+ const selectKeys = dataKeyItems.map((item, index) => {
75
+ const aliasName = `key0${index + 1}`
76
+ const columnName = `summary.${aliasName}`
77
+
78
+ return `${columnName} as ${aliasName}`
79
+ }) as string[]
80
+ const selectData = dataItems.map((item, index) => {
81
+ const aliasName = `data0${index + 1}`
82
+ const columnName = `summary.${aliasName}`
83
+
84
+ switch (item.agg) {
85
+ case STAT_FUNCTION_MAP.sum:
86
+ return `SUM(${columnName}) as ${aliasName}`
87
+ case STAT_FUNCTION_MAP.mean:
88
+ // 일차 mean 된 값들을 다시 계산하므로, 무의미할 수 있다.
89
+ return `AVG(${columnName}) as ${aliasName}`
90
+ case STAT_FUNCTION_MAP.stddev:
91
+ // 일차 stddev 된 값들을 다시 계산하므로, 무의미할 수 있다.
92
+ return `STDDEV(${columnName}) as ${aliasName}`
93
+ case STAT_FUNCTION_MAP.variance:
94
+ // 일차 variance 된 값들을 다시 계산하므로, 무의미할 수 있다.
95
+ return `VARIANCE(${columnName}) as ${aliasName}`
96
+ case STAT_FUNCTION_MAP.min:
97
+ return `MIN(${columnName}) as ${aliasName}`
98
+ case STAT_FUNCTION_MAP.max:
99
+ return `MAX(${columnName}) as ${aliasName}`
100
+ case STAT_FUNCTION_MAP.range:
101
+ // 일차 range 값들을 다시 계산하므로, 무의미할 수 있다.
102
+ return `MAX(${columnName}) - MIN(${columnName}) as ${aliasName}`
103
+ case STAT_FUNCTION_MAP.mode:
104
+ // not guaranteed
105
+ return `MODE() WITHIN GROUP (ORDER BY ${columnName})`
106
+ default:
107
+ return `AVG(${columnName}) as ${aliasName}`
108
+ }
109
+ })
110
+
111
+ const groupByPeriod =
112
+ period == 'today' ? 'summary.date, summary.period' : ['this year', '12 months'].includes(period) ? 'SUBSTRING(summary.date, 1, 7)' : 'summary.date'
113
+ const groupByKeys = dataKeyItems
114
+ .map((item, index) => {
115
+ return `summary.key0${index + 1}`
116
+ })
117
+ .join(', ')
118
+
119
+ const desc = false
120
+ var orderByPeriod: Sorting[] =
121
+ period == 'today'
122
+ ? [
123
+ {
124
+ name: 'date',
125
+ desc
126
+ },
127
+ {
128
+ name: 'period',
129
+ desc
130
+ }
131
+ ]
132
+ : ['this year', '12 months'].includes(period)
133
+ ? [{ name: 'month', desc }]
134
+ : [{ name: 'date', desc }]
135
+
136
+ if (params?.sortings && params.sortings.length > 0) {
137
+ orderByPeriod = params.sortings
138
+ }
139
+
140
+ var queryBuilder = getQueryBuilderFromListParams({
141
+ repository: getRepository(DataSummary),
142
+ params: {
143
+ ...params,
144
+ sortings: []
145
+ },
146
+ domain,
147
+ alias: 'summary',
148
+ searchables: searchables
149
+ })
150
+ .select('summary.dataSet')
151
+ .addSelect(selectData)
152
+ .addSelect('SUM(summary.count)', 'count')
153
+ .addSelect('SUM(summary.countOoc)', 'countOoc')
154
+ .addSelect('SUM(summary.countOos)', 'countOos')
155
+
156
+ queryBuilder = selectPeriod(queryBuilder)
157
+ .innerJoin('summary.dataSet', 'ds', 'ds.id = :dataSetName', {
158
+ dataSetName: dataSet.id
159
+ })
160
+ .andWhere('summary.domain = :domain', { domain: domain.id })
161
+ .andWhere('summary.date >= :from', { from })
162
+ .andWhere('summary.date < :to', { to })
163
+ .addGroupBy('summary.dataSet')
164
+ .addGroupBy(groupByPeriod)
165
+
166
+ if (dataKeyItems.length > 0) {
167
+ queryBuilder.addSelect(selectKeys).addGroupBy(groupByKeys)
168
+ }
169
+
170
+ orderByPeriod.map(orderBy => {
171
+ const { name: sort, desc } = orderBy
172
+ queryBuilder.addOrderBy(sort, desc ? 'DESC' : 'ASC')
173
+ })
174
+
175
+ const items = await queryBuilder.getRawMany()
176
+
177
+ return items
178
+ }
@@ -0,0 +1,54 @@
1
+ import { config } from '@things-factory/env'
2
+ import { getEndpointUrl } from '../utils/config-resolver'
3
+
4
+ import debugLib from 'debug'
5
+ const debug = debugLib('things-factory:dataset:shiny-report')
6
+ const dataReportConfig = config.get('dataReport')
7
+ const {
8
+ shiny: {
9
+ endpoint: ENDPOINT,
10
+ datasource: { database: DATABASE }
11
+ }
12
+ } = Object.assign(
13
+ {
14
+ shiny: {
15
+ endpoint: {},
16
+ datasource: {}
17
+ }
18
+ },
19
+ dataReportConfig
20
+ )
21
+
22
+ export async function renderShinyReport(context: any) {
23
+ debug('render:context', context)
24
+
25
+ try {
26
+ const cookies = new URLSearchParams(context.header.cookie.replace(/; /g, '&'))
27
+ const token = cookies.get('access_token')
28
+ const {
29
+ state: { domain },
30
+ query,
31
+ response
32
+ } = context
33
+
34
+ const urlParams = new URLSearchParams({
35
+ token,
36
+ ...query,
37
+ database: DATABASE,
38
+ domain: domain?.subdomain
39
+ }).toString()
40
+
41
+ const subpath = query?.reportView || ''
42
+
43
+ if (!subpath) {
44
+ return (context.body = '')
45
+ }
46
+
47
+ const url = getEndpointUrl(ENDPOINT, subpath)
48
+ const reportUrl = `${url}/?${urlParams}`
49
+
50
+ response.redirect(reportUrl)
51
+ } catch (ex) {
52
+ context.body = `Error: ${ex.message} || ''`
53
+ }
54
+ }
@@ -0,0 +1 @@
1
+ import './task'
@@ -0,0 +1,100 @@
1
+ import { TaskRegistry, InputStep, Context } from '@things-factory/integration-base'
2
+ import { getDataSource } from '@things-factory/shell'
3
+ import { access } from '@things-factory/utils'
4
+ import i18next from 'i18next'
5
+
6
+ import { DataSet } from '../../service/data-set/data-set'
7
+
8
+ import { createDataSample } from '../../controllers/create-data-sample'
9
+ import { TaskHandler } from '@things-factory/integration-base/dist-server/engine/types'
10
+
11
+ async function CreateDataSample(step: InputStep, context: Context) {
12
+ const { data, domain, user, lng } = context
13
+ var {
14
+ params: {
15
+ dataset: dataSetId,
16
+ source: sourceAccessor,
17
+ rawData: rawDataAccessor,
18
+ data: dataAccessor,
19
+ timestamp: timestampAccessor
20
+ }
21
+ } = step
22
+
23
+ if (!dataSetId) {
24
+ throw new Error(`no dataset found`)
25
+ }
26
+
27
+ // make new data-sample
28
+ return await getDataSource().transaction(async tx => {
29
+ const dataSet = await tx.getRepository(DataSet).findOne({
30
+ where: {
31
+ id: dataSetId
32
+ }
33
+ })
34
+
35
+ var source = await access(sourceAccessor, data)
36
+ var extractedData = await access(dataAccessor, data)
37
+ var extractedRawData = rawDataAccessor && (await access(rawDataAccessor, data))
38
+ var timestamp = timestampAccessor && (await access(timestampAccessor, data))
39
+
40
+ const dataSample = await createDataSample(
41
+ {
42
+ dataSet,
43
+ data: extractedData,
44
+ rawData: extractedRawData,
45
+ source,
46
+ collectedAt: new Date(timestamp || new Date())
47
+ },
48
+ {
49
+ t: i18next.t,
50
+ state: {
51
+ domain,
52
+ user,
53
+ lng,
54
+ tx
55
+ }
56
+ }
57
+ )
58
+
59
+ return {
60
+ data: dataSample
61
+ }
62
+ })
63
+ }
64
+
65
+ CreateDataSample.parameterSpec = [
66
+ {
67
+ type: 'entity-selector',
68
+ name: 'dataset',
69
+ label: 'data-set',
70
+ property: {
71
+ queryName: 'dataSets',
72
+ valueKey: 'id'
73
+ }
74
+ },
75
+ {
76
+ type: 'string',
77
+ name: 'source',
78
+ label: 'source'
79
+ },
80
+ {
81
+ type: 'string',
82
+ name: 'rawData',
83
+ label: 'raw-data'
84
+ },
85
+ {
86
+ type: 'string',
87
+ name: 'data',
88
+ label: 'data'
89
+ },
90
+ {
91
+ type: 'string',
92
+ name: 'timestamp',
93
+ label: 'timestamp'
94
+ }
95
+ ]
96
+
97
+ CreateDataSample.connectorFree = true
98
+ CreateDataSample.help = 'dataset/task/create-data-sample'
99
+
100
+ TaskRegistry.registerTaskHandler('create-data-sample', CreateDataSample as TaskHandler)
@@ -0,0 +1,2 @@
1
+ import './create-data-sample'
2
+ import './issue-collect-data'
@@ -0,0 +1,45 @@
1
+ import { TaskRegistry, InputStep, Context } from '@things-factory/integration-base'
2
+ import { issueDataCollectionTask } from '../../controllers/issue-data-collection-task'
3
+ import i18next from 'i18next'
4
+
5
+ async function IssueCollectData(step: InputStep, context: Context) {
6
+ const { logger, data, domain, user, lng } = context
7
+ var {
8
+ params: { dataset: dataSetId }
9
+ } = step
10
+
11
+ if (!dataSetId) {
12
+ throw new Error(`no dataset found`)
13
+ }
14
+
15
+ const activityInstance = await issueDataCollectionTask(domain.id, dataSetId, {
16
+ t: i18next.t,
17
+ state: {
18
+ domain,
19
+ user,
20
+ lng,
21
+ tx: undefined
22
+ }
23
+ })
24
+
25
+ return {
26
+ data: activityInstance
27
+ }
28
+ }
29
+
30
+ IssueCollectData.parameterSpec = [
31
+ {
32
+ type: 'entity-selector',
33
+ name: 'dataset',
34
+ label: 'data-set',
35
+ property: {
36
+ queryName: 'dataSets',
37
+ valueKey: 'id'
38
+ }
39
+ }
40
+ ]
41
+
42
+ IssueCollectData.connectorFree = true
43
+ IssueCollectData.help = 'dataset/task/issue-collect-data'
44
+
45
+ TaskRegistry.registerTaskHandler('issue-collect-data', IssueCollectData)
@@ -0,0 +1,8 @@
1
+ import './routes'
2
+ import './activities'
3
+ import './engine'
4
+
5
+ export * from './controllers'
6
+ export * from './service'
7
+
8
+ export * from './service/data-spec/data-spec-manager'
@@ -0,0 +1,188 @@
1
+ import { In } from 'typeorm'
2
+
3
+ import { Domain, getDataSource } from '@things-factory/shell'
4
+ import { User } from '@things-factory/auth-base'
5
+ import { runScenario } from '@things-factory/integration-base'
6
+ import { ScheduleRegisterRequest } from '@things-factory/scheduler-client'
7
+
8
+ import { createDataSample } from './controllers/create-data-sample'
9
+ import { renderJasperReport } from './controllers/jasper-report'
10
+ import { renderShinyReport } from './controllers/shiny-report'
11
+ import { issueDataCollectionTask } from './controllers/issue-data-collection-task'
12
+ import { DataSensor } from './service/data-sensor/data-sensor'
13
+ import { DataSet } from './service/data-set/data-set'
14
+
15
+ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
16
+ /*
17
+ * can add global public routes to application (auth not required, tenancy not required)
18
+ *
19
+ * ex) routes.get('/path', async(context, next) => {})
20
+ * ex) routes.post('/path', async(context, next) => {})
21
+ */
22
+
23
+ globalPublicRouter.post('/sensor-data', async (context, next) => {
24
+ // 데이타 검증
25
+ const { deviceId, data, rawData, timestamp = Date.now() } = context.request.body
26
+ if (!deviceId) {
27
+ throw new Error(`The deviceId(${deviceId}) property are mandatory`)
28
+ }
29
+
30
+ // make new data-sample
31
+ await getDataSource().transaction(async tx => {
32
+ // find sensor through deviceId
33
+ const sensor = await tx.getRepository(DataSensor).findOne({
34
+ where: { deviceId },
35
+ relations: ['domain', 'appliance', 'dataSet', 'decoder']
36
+ })
37
+
38
+ if (!sensor) {
39
+ throw new Error(`Sensor having given deviceId(${deviceId}) not found`)
40
+ }
41
+
42
+ if (!sensor.active) {
43
+ throw new Error(`State of the sensor given deviceId(${deviceId}) is not active`)
44
+ }
45
+
46
+ if (!sensor.appliance) {
47
+ throw new Error(`Appliance of the sensor given deviceId(${deviceId}) is not set up`)
48
+ }
49
+
50
+ const domain = sensor.domain
51
+ const dataSet = sensor.dataSet
52
+
53
+ /*
54
+ 원칙적으로 user 인증정보를 가지고 요청해야 하지만, 인증정보를 보낼 수 없는 상황이라고 전제하여 처리한다.
55
+ 앞으로, 보완되어야 한다.
56
+ */
57
+ const user: User =
58
+ context.user ||
59
+ (await tx.getRepository(User).findOne({
60
+ where: {
61
+ reference: sensor.appliance.id,
62
+ userType: 'appliance'
63
+ }
64
+ }))
65
+
66
+ context.state = {
67
+ user,
68
+ ...context.state,
69
+ domain,
70
+ tx
71
+ }
72
+
73
+ var decoded
74
+ if (sensor.decoder) {
75
+ const { name: scenarioName } = sensor.decoder
76
+ const variables = {
77
+ dataSensor: sensor,
78
+ source: deviceId,
79
+ data,
80
+ rawData,
81
+ timestamp
82
+ }
83
+
84
+ decoded = (await runScenario(null, scenarioName, variables, context))?.result
85
+ }
86
+
87
+ if (dataSet) {
88
+ await createDataSample(
89
+ {
90
+ dataSet,
91
+ data,
92
+ rawData,
93
+ source: deviceId,
94
+ collectedAt: new Date(timestamp),
95
+ ...decoded
96
+ },
97
+ context
98
+ )
99
+ }
100
+
101
+ context.status = 200
102
+ context.body = {
103
+ data,
104
+ ...decoded
105
+ }
106
+ })
107
+ })
108
+
109
+ /* When a callback occurs from the scheduler when a scheduled dataset is on schedule, data collection task for the dataset should be issued. */
110
+ globalPublicRouter.post('/callback-schedule-for-dataset', async (context, next) => {
111
+ const { client } = context.request.body as ScheduleRegisterRequest
112
+
113
+ if (!client || typeof client !== 'object') {
114
+ throw new Error('client property should be a part of callback body.')
115
+ }
116
+
117
+ const { group: domainId, key: dataSetId } = client
118
+
119
+ if (!domainId || !dataSetId) {
120
+ throw new Error(`group(${domainId}) and key(${dataSetId}) properties should not be empty`)
121
+ }
122
+
123
+ await issueDataCollectionTask(domainId, dataSetId, context)
124
+
125
+ context.status = 200
126
+ })
127
+
128
+ /* When a callback occurs from the scheduler when a scheduled summary is on schedule, data summary procedure for the dataset should be executed. */
129
+ globalPublicRouter.post('/callback-schedule-for-data-summary', async (context, next) => {
130
+ const { client } = context.request.body as ScheduleRegisterRequest
131
+
132
+ if (!client || typeof client !== 'object') {
133
+ throw new Error('client property should be a part of callback body.')
134
+ }
135
+
136
+ const { group: domainId, key: dataSetId } = client
137
+
138
+ if (!domainId || !dataSetId) {
139
+ throw new Error(`group(${domainId}) and key(${dataSetId}) properties should not be empty`)
140
+ }
141
+
142
+ await getDataSource().transaction(async tx => {
143
+ const domain = await tx.getRepository(Domain).findOneBy({ id: domainId })
144
+
145
+ if (!domain) {
146
+ throw new Error(`domain(${domainId}) not found`)
147
+ }
148
+
149
+ const dataSet = await tx
150
+ .getRepository(DataSet)
151
+ .findOne({ where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id: dataSetId } })
152
+
153
+ // do what you gotta do
154
+ })
155
+
156
+ context.status = 200
157
+ })
158
+ })
159
+
160
+ process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
161
+ /*
162
+ * can add global private routes to application (auth required, tenancy not required)
163
+ */
164
+ })
165
+
166
+ process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
167
+ /*
168
+ * can add domain public routes to application (auth not required, tenancy required)
169
+ */
170
+ })
171
+
172
+ process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
173
+ /*
174
+ * can add domain private routes to application (auth required, tenancy required)
175
+ */
176
+ domainPrivateRouter.get('/data-report/jasper', async (context, next) => {
177
+ context.body = await renderJasperReport(context)
178
+ })
179
+
180
+ domainPrivateRouter.get('/data-report/shiny', async (context, next) => {
181
+ await renderShinyReport(context)
182
+ })
183
+
184
+ domainPrivateRouter.get('/data-report/:reportType', async (context, next) => {
185
+ let { reportType } = context.params
186
+ context.body = `${reportType} Not Implemented.`
187
+ })
188
+ })