@things-factory/dataset 6.0.0-alpha.0 → 6.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -19
- package/assets/diagram.jpg +0 -0
- package/client/pages/data-archive/data-archive-list-page.js +290 -0
- package/client/pages/data-archive/data-archive-request-popup.js +175 -0
- package/client/pages/data-key-set/data-key-set-list-page.js +1 -1
- package/client/pages/data-report/data-report-list-page.js +2 -2
- package/client/pages/data-set/data-set-list-page.js +1 -1
- package/client/route.js +4 -0
- package/config/config.development.js +11 -0
- package/config/config.production.js +11 -0
- package/dist-server/controllers/jasper-report.js +4 -4
- package/dist-server/controllers/jasper-report.js.map +1 -1
- package/dist-server/controllers/shiny-report.js +4 -7
- package/dist-server/controllers/shiny-report.js.map +1 -1
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/data-archive/data-archive-mutation.js +227 -0
- package/dist-server/service/data-archive/data-archive-mutation.js.map +1 -0
- package/dist-server/service/data-archive/data-archive-query.js +83 -0
- package/dist-server/service/data-archive/data-archive-query.js.map +1 -0
- package/dist-server/service/data-archive/data-archive-type.js +74 -0
- package/dist-server/service/data-archive/data-archive-type.js.map +1 -0
- package/dist-server/service/data-archive/data-archive.js +79 -0
- package/dist-server/service/data-archive/data-archive.js.map +1 -0
- package/dist-server/service/data-archive/index.js +9 -0
- package/dist-server/service/data-archive/index.js.map +1 -0
- package/dist-server/service/data-set/data-set-mutation.js +1 -1
- package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
- package/dist-server/service/index.js +5 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/config-resolver.js +33 -0
- package/dist-server/utils/config-resolver.js.map +1 -0
- package/dist-server/utils/index.js +5 -0
- package/dist-server/utils/index.js.map +1 -0
- package/helps/dataset/data-archive.md +9 -0
- package/helps/dataset/data-entry-list.md +2 -0
- package/helps/dataset/data-key-set.md +10 -0
- package/helps/dataset/data-ooc.md +11 -0
- package/helps/dataset/data-report-list.md +4 -0
- package/helps/dataset/data-sample-search.md +2 -0
- package/helps/dataset/data-sample.md +11 -0
- package/helps/dataset/data-sensor.md +13 -0
- package/helps/dataset/data-set.md +28 -0
- package/helps/dataset/ui/data-item-list.md +19 -0
- package/helps/dataset/ui/data-key-item-list.md +2 -0
- package/package.json +8 -8
- package/server/controllers/jasper-report.ts +5 -4
- package/server/controllers/shiny-report.ts +4 -7
- package/server/routes.ts +1 -0
- package/server/service/data-archive/data-archive-mutation.ts +282 -0
- package/server/service/data-archive/data-archive-query.ts +55 -0
- package/server/service/data-archive/data-archive-type.ts +49 -0
- package/server/service/data-archive/data-archive.ts +69 -0
- package/server/service/data-archive/index.ts +6 -0
- package/server/service/data-set/data-set-mutation.ts +1 -1
- package/server/service/index.ts +5 -2
- package/server/utils/config-resolver.ts +29 -0
- package/server/utils/index.ts +1 -0
- package/things-factory.config.js +4 -0
- package/translations/en.json +18 -3
- package/translations/ko.json +15 -1
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getEndpointUrl = void 0;
|
4
|
+
function getEndpointUrl(endpoint, subpath = '') {
|
5
|
+
const { url = '', protocol = 'http', host = '', port = 80 } = endpoint;
|
6
|
+
let _url = '';
|
7
|
+
let _protocol = protocol;
|
8
|
+
if (url) {
|
9
|
+
_url = url;
|
10
|
+
}
|
11
|
+
else {
|
12
|
+
const hosts = host.split('://');
|
13
|
+
// host에 protocol을 포함한 경우
|
14
|
+
if (hosts.length == 2) {
|
15
|
+
const _host = hosts[1];
|
16
|
+
_protocol = hosts[0];
|
17
|
+
// host에 port를 포함한 경우
|
18
|
+
const _hosts = _host.split(':');
|
19
|
+
if (_hosts.length > 1) {
|
20
|
+
_url = `${_hosts[0]}:${_hosts[1]}`;
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
_url = `${_host}:${port}`;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
else if (hosts.length == 1) {
|
27
|
+
_url = `${host}:${port}`;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
return _protocol + '://' + _url.split('/').concat(subpath.split('/')).filter(x => x).join('/');
|
31
|
+
}
|
32
|
+
exports.getEndpointUrl = getEndpointUrl;
|
33
|
+
//# sourceMappingURL=config-resolver.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"config-resolver.js","sourceRoot":"","sources":["../../server/utils/config-resolver.ts"],"names":[],"mappings":";;;AACA,SAAgB,cAAc,CAAC,QAAY,EAAE,UAAe,EAAE;IAC1D,MAAM,EAAE,GAAG,GAAC,EAAE,EAAE,QAAQ,GAAC,MAAM,EAAE,IAAI,GAAC,EAAE,EAAE,IAAI,GAAC,EAAE,EAAE,GAAG,QAAQ,CAAA;IAE9D,IAAI,IAAI,GAAG,EAAE,CAAA;IACb,IAAI,SAAS,GAAG,QAAQ,CAAA;IAExB,IAAI,GAAG,EAAE;QACL,IAAI,GAAG,GAAG,CAAA;KACb;SAAM;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC/B,yBAAyB;QACzB,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE;YACnB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACtB,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACpB,qBAAqB;YACrB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAC/B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;gBACnB,IAAI,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;aACrC;iBAAM;gBACH,IAAI,GAAG,GAAG,KAAK,IAAI,IAAI,EAAE,CAAA;aAC5B;SACJ;aAAM,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE;YAC1B,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAA;SAC3B;KACJ;IAED,OAAO,SAAS,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAChG,CAAC;AA3BD,wCA2BC","sourcesContent":["\nexport function getEndpointUrl(endpoint:any, subpath:string=''):string {\n const { url='', protocol='http', host='', port=80 } = endpoint\n \n let _url = ''\n let _protocol = protocol\n\n if (url) {\n _url = url\n } else {\n const hosts = host.split('://')\n // host에 protocol을 포함한 경우\n if (hosts.length == 2) {\n const _host = hosts[1]\n _protocol = hosts[0]\n // host에 port를 포함한 경우\n const _hosts = _host.split(':')\n if (_hosts.length > 1) {\n _url = `${_hosts[0]}:${_hosts[1]}`\n } else {\n _url = `${_host}:${port}`\n }\n } else if (hosts.length == 1) { \n _url = `${host}:${port}`\n }\n }\n\n return _protocol + '://' + _url.split('/').concat(subpath.split('/')).filter(x=>x).join('/')\n}\n"]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/utils/index.ts"],"names":[],"mappings":";;;AAAA,4DAAiC","sourcesContent":["export * from './config-resolver'"]}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# DataArchive
|
2
|
+
데이터셋의 원형 데이터(raw data)를 csv.gz 파일로 다운로드할 수 있습니다. <br>
|
3
|
+
요청 후 시간이 수 분 소요될 수 있으며, 정상적으로 요청이 수행되면 다운로드 가능한 링크를 알림으로 알려줍니다. 알림의 링크를 클릭하여 파일을 다운로드 할 수 있습니다. <br>
|
4
|
+
조회 페이지에서도 다운로드 주소를 확인할 수 있습니다. <br>
|
5
|
+
다운로드 링크는 유효기간이 있으며, 만료되면 더 이상 다운로드할 수 없습니다. <br>
|
6
|
+
다음의 경우들은 관리자에게 문의하시기 바랍니다.
|
7
|
+
- 요청시간 초과(timeout)가 발생
|
8
|
+
- 10분 경과 후에도 상태가 requested에서 변경되지 않음
|
9
|
+
- 최종 상태가 error로 표기
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# DataKeySet
|
2
|
+
데이터 키셋은 데이터샘플 조회에 인덱싱을 하기 위한 장치입니다. 데이터샘플 레코드의 data컬럼에 저장되는 오브젝트 key값을 기준으로 정보를 추가합니다. 데이터 키셋은 데이터셋에 지정할 수 있습니다. <br>
|
3
|
+
- 이름: 데이타 키셋 이름을 설정합니다. <br>
|
4
|
+
- 설명: 데이타 키셋에 대한 설명을 설정합니다. <br>
|
5
|
+
- 활성화: 사용 여부를 설정합니다. <br>
|
6
|
+
- 리포트용 화면종류:
|
7
|
+
- Jasper: Jasper 서버 설정에 따른 jasper 페이지를 렌더링합니다.
|
8
|
+
- Shiny: shiny 서버 설정에 따른 shiny 페이지를 렌더링합니다.
|
9
|
+
|
10
|
+
- 리포트용 화면: 리포트용 화면종류에 따른 값을 설정합니다. Jasper나 Shiny의 경우, 해당 서버에서 유효한 suburl이 필요합니다.<br>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# DataOOC
|
2
|
+
데이타 이탈 내역을 조회하고 조치합니다.
|
3
|
+
|
4
|
+
- 데이타 이탈점 상세: 데이타 이탈점 상세 팝업 화면을 띄웁니다.
|
5
|
+
- 데이타 셋 아이템 별 CCP, QC 이탈 여부를 조회합니다.
|
6
|
+
- 데이타 이탈점 생성 부터 조치 이력을 조회합니다.
|
7
|
+
- 조치, 조치완료 내용을 입력하고 저장합니다.
|
8
|
+
- 상태: 생성, 조치, 조치완료 상태를 보여줍니다.
|
9
|
+
- 조치 활동: 조치 내용을 보여줍니다.
|
10
|
+
|
11
|
+
나머지는 항목들은 [데이타 샘플 조회](./data-sample.md)와 동일합니다.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# DataSample
|
2
|
+
데이타 샘플을 조회합니다.
|
3
|
+
- 이름: 데이타 샘플이 사용하는 데이타 셋의 이름입니다.
|
4
|
+
- 설명: 데이타 샘플이 사용하는 데이타 셋의 설명입니다.
|
5
|
+
- 데이타키 #: 데이타 키셋에서 정의한 데이타 키 목록이 순서대로 저장된 값입니다. 데이타 샘플의 data 오브젝트에 해당 키, 값이 포함되어 있어야 합니다.
|
6
|
+
- 관리한계 이탈여부: 데이타 샘플이 사용하는 데이타 셋의 항목 중 CCP 명세를 벗어나는 것이 있으면 true로 저장됩니다.
|
7
|
+
- 허용한계 이탈여부: 데이타 샘플이 사용하는 데이타 셋의 항목 중 QC 명세를 벗어나는 것이 있으면 true로 저장됩니다.
|
8
|
+
- 데이타: 키, 값 쌍으로 된 json형태의 가공된 데이터입니다.
|
9
|
+
- 데이타 원본: 가공하기 전, 데이타 원본(Raw Data)이 저장됩니다.
|
10
|
+
- 작업기준일: 교대 작업조 기준의 작업일입니다.
|
11
|
+
- 교대근무조: 교대 작업조 기준 시간의 작업조 이름입니다.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# DataSensor
|
2
|
+
데이타 센서를 등록하고 조회합니다.<br>
|
3
|
+
- 이름: 데이타 센서 이름을 설정합니다.<br>
|
4
|
+
- 설명: 데이타 센서에 대한 설명을 설정합니다.<br>
|
5
|
+
- 활성화: 사용 여부를 설정합니다. 비활성화된 센서 데이타는 데이타 샘플로 저장하지 않습니다.<br>
|
6
|
+
- 디바이스 아이디: 유일한 센서의 아이디를 설정합니다 <br>
|
7
|
+
- 태그이름: (deprecated 검토 중)<br>
|
8
|
+
- 어플라이언스: 데이타 등록 권한을 가진 어플라이언스를 설정합니다.<br>
|
9
|
+
- 데이터셋: 해당 센서 데이터에 반영할 데이터셋을 설정합니다. <br>
|
10
|
+
- 모델: 센서 모델 정보를 설정합니다.<br>
|
11
|
+
- 브랜드: 센서 브랜드를 설정합니다.<br>
|
12
|
+
- 시리얼번호: 센서 시리얼번호를 설정합니다.<br>
|
13
|
+
- 네트워크마스크: netmask 정보를 설정합니다.<br>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# DataSet
|
2
|
+
데이타 셋을 정의합니다.<br>
|
3
|
+
- [`데이타 아이템 등록 및 조회`](./ui/data-key-item-list.md): 데이타 아이템 화면을 팝업으로 띄웁니다.
|
4
|
+
- 데이타 입력: 데이타 입력 화면을 팝업으로 띄웁니다.
|
5
|
+
- 이름: 데이터셋 이름을 설정합니다.<br>
|
6
|
+
- 설명: 데이터셋에 대한 설명을 설정합니다.<br>
|
7
|
+
- 유형: 데이터셋 유형을 설정합니다.<br>
|
8
|
+
- 수동 수집: 사용자가 입력하는 데이터셋인 경우
|
9
|
+
- 자동 수집: 센서처럼 자동으로 입력되는 데이터셋인 경우
|
10
|
+
- 데이타 키셋: 데이타 키셋 마스터 페이지에서 등록한 항목을 적용합니다.<br>
|
11
|
+
- 파티션 키: (deprecated 예정)<br>
|
12
|
+
- 스케줄: 입력 주기를 설정합니다.<br>
|
13
|
+
- 타임존: 수집일 타임존을 설정합니다.<br>
|
14
|
+
- 관리자 역할: 데이터셋 관리자 역할을 설정합니다.<br>
|
15
|
+
- 입력담당 역할: 입력 권한을 부여할 역할을 설정합니다.<br>
|
16
|
+
- 입력용 화면종류: 입력 화면종류를 설정합니다. 주로 Board가 사용됩니다.<br>
|
17
|
+
- Generated: 구현된 화면을 사용합니다.
|
18
|
+
- Board: Board 화면을 사용합니다.
|
19
|
+
- Page: 구현된 페이지로 이동합니다. suburl이 필요합니다.
|
20
|
+
- External URL: 외부 페이지로 이동합니다. 전체 Url이 필요합니다.
|
21
|
+
- 입력용 화면: 화면 종류에 따른 값을 설정합니다. Board 화면 종류는 Board를 선택할 수 있습니다.<br>
|
22
|
+
- 모니터용 화면종류: 모니터용 화면종류를 설정합니다. 선택 목록은 입력용 화면종류와 동일합니다.<br>
|
23
|
+
- 모니터용 화면: 모니터용 화면종류에 해당하는 값을 설정합니다. <br>
|
24
|
+
- 리포트용 화면종류: 리포트용 화면종류를 설정합니다. 선택 목록은 입력용 화면종류에 두 종류가 더 있습니다.
|
25
|
+
- Jasper: Jasper 서버 설정에 따른 jasper 페이지를 렌더링합니다.
|
26
|
+
- Shiny: shiny 서버 설정에 따른 shiny 페이지를 렌더링합니다.
|
27
|
+
- 리포트용 화면: 리포트용 화면종류에 맞는 값을 설정합니다. Jasper나 Shiny의 경우, 해당 서버에서 유효한 suburl이 필요합니다. <br>
|
28
|
+
- 리포트용 템플릿: 리포트용 화면에 필요한 템플릿 파일을 업로드합니다.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# DataItemList
|
2
|
+
데이타 셋의 아이템 목록을 등록합니다.
|
3
|
+
- 이름: 데이타 셋 아이템 이름입니다.
|
4
|
+
- 설명: 데이타 셋 아아템 설명입니다.
|
5
|
+
- 활성화: 사용 여부를 설정합니다.
|
6
|
+
- 숨기기: 레포트 등 2차 가공 데이터로 사용시, 사용자에게 보여줄 지 여부를 설정합니다.
|
7
|
+
- 태그이름: 오브젝트 키를 설정합니다.
|
8
|
+
- 유형: 데이타 셋 아이템 값의 데이타 타입 입니다.
|
9
|
+
- number
|
10
|
+
- text
|
11
|
+
- select
|
12
|
+
- boolean
|
13
|
+
- file
|
14
|
+
- Options: select 유형인 경우, name과 value 쌍을 추가로 설정합니다.
|
15
|
+
- 단위: kg, cm, 등 의 입력 단위를 설정합니다.
|
16
|
+
- 샘플수: 해당 아이템 값을 몇 개 입력받을지 설정합니다.
|
17
|
+
- 명세: 명세를 정의하면 이탈값이 OOC 데이터로 추가됩니다. 또한 해당 데이터샘플에 이탈여부가 기록됩니다.
|
18
|
+
- CCP: Critical Control Point Data Spec을 정의합니다.
|
19
|
+
- QC: Quality Contol Data Spec을 정의합니다.
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@things-factory/dataset",
|
3
|
-
"version": "6.0.0-alpha.
|
3
|
+
"version": "6.0.0-alpha.10",
|
4
4
|
"main": "dist-server/index.js",
|
5
5
|
"browser": "client/index.js",
|
6
6
|
"things-factory": true,
|
@@ -34,14 +34,14 @@
|
|
34
34
|
"@operato/shell": "^1.0.1",
|
35
35
|
"@operato/styles": "^1.0.0",
|
36
36
|
"@operato/utils": "^1.0.1",
|
37
|
-
"@things-factory/auth-base": "^6.0.0-alpha.
|
38
|
-
"@things-factory/aws-base": "^6.0.0-alpha.
|
39
|
-
"@things-factory/board-service": "^6.0.0-alpha.
|
40
|
-
"@things-factory/env": "^6.0.0-alpha.
|
41
|
-
"@things-factory/shell": "^6.0.0-alpha.
|
42
|
-
"@things-factory/work-shift": "^6.0.0-alpha.
|
37
|
+
"@things-factory/auth-base": "^6.0.0-alpha.10",
|
38
|
+
"@things-factory/aws-base": "^6.0.0-alpha.10",
|
39
|
+
"@things-factory/board-service": "^6.0.0-alpha.10",
|
40
|
+
"@things-factory/env": "^6.0.0-alpha.8",
|
41
|
+
"@things-factory/shell": "^6.0.0-alpha.8",
|
42
|
+
"@things-factory/work-shift": "^6.0.0-alpha.10",
|
43
43
|
"cron-parser": "^4.3.0",
|
44
44
|
"moment-timezone": "^0.5.34"
|
45
45
|
},
|
46
|
-
"gitHead": "
|
46
|
+
"gitHead": "9ec9c564e157ebe830839e5a9cf8cfd6ec4e44b1"
|
47
47
|
}
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import FormData from 'form-data'
|
2
2
|
import fetch from 'node-fetch'
|
3
3
|
|
4
|
+
import { getEndpointUrl } from '../utils/config-resolver'
|
5
|
+
|
4
6
|
import { STORAGE } from '@things-factory/attachment-base'
|
5
7
|
import { AthenaController } from '@things-factory/aws-base'
|
6
8
|
import { config } from '@things-factory/env'
|
@@ -8,7 +10,7 @@ import { config } from '@things-factory/env'
|
|
8
10
|
const dataReportConfig = config.get('dataReport')
|
9
11
|
const {
|
10
12
|
jasper: {
|
11
|
-
endpoint:
|
13
|
+
endpoint: ENDPOINT,
|
12
14
|
datasource: { database: DATABASE }
|
13
15
|
}
|
14
16
|
} = dataReportConfig || {
|
@@ -173,9 +175,8 @@ export async function renderJasperReport(context: any) {
|
|
173
175
|
formData.append('parameters', JSON.stringify(parameters))
|
174
176
|
|
175
177
|
const { reportView } = query
|
176
|
-
const
|
177
|
-
const
|
178
|
-
const response = await fetch(reportUrl, {
|
178
|
+
const url = getEndpointUrl(ENDPOINT, reportView)
|
179
|
+
const response = await fetch(url, {
|
179
180
|
method: 'POST',
|
180
181
|
body: formData
|
181
182
|
})
|
@@ -1,13 +1,14 @@
|
|
1
1
|
import FormData from 'form-data'
|
2
2
|
import fetch from 'node-fetch'
|
3
3
|
import { config } from '@things-factory/env'
|
4
|
+
import { getEndpointUrl } from '../utils/config-resolver'
|
4
5
|
|
5
6
|
const querystring = require('querystring')
|
6
7
|
const debug = require('debug')('things-factory:dataset:shiny-report')
|
7
8
|
const dataReportConfig = config.get('dataReport')
|
8
9
|
const {
|
9
10
|
shiny: {
|
10
|
-
endpoint:
|
11
|
+
endpoint: ENDPOINT,
|
11
12
|
datasource: { database: DATABASE }
|
12
13
|
}
|
13
14
|
} = Object.assign({
|
@@ -41,19 +42,15 @@ export async function renderShinyReport(context: any) {
|
|
41
42
|
// domain: domain?.subdomain }
|
42
43
|
// })
|
43
44
|
|
44
|
-
const protocol = `${PROTOCOL || 'http'}://`
|
45
|
-
const host = `${HOST}:${PORT}/`
|
46
45
|
const subpath = query?.reportView || ''
|
47
46
|
|
48
47
|
if (!subpath) {
|
49
48
|
return context.body = ''
|
50
49
|
}
|
51
50
|
|
52
|
-
|
53
|
-
const url = host.split('/').concat(subpath.split('/'))
|
54
|
-
.filter(x => x).join('/')
|
51
|
+
const url = getEndpointUrl(ENDPOINT, subpath)
|
55
52
|
// const reportUrl = `${protocol}${url}/?params=${jsonParams}`
|
56
|
-
const reportUrl = `${
|
53
|
+
const reportUrl = `${url}/?${urlParams}`
|
57
54
|
|
58
55
|
response.redirect(reportUrl)
|
59
56
|
} catch(ex) {
|
package/server/routes.ts
CHANGED
@@ -0,0 +1,282 @@
|
|
1
|
+
import i18next from 'i18next'
|
2
|
+
import fetch from 'node-fetch'
|
3
|
+
import {
|
4
|
+
Arg,
|
5
|
+
Ctx,
|
6
|
+
Directive,
|
7
|
+
Mutation,
|
8
|
+
Resolver
|
9
|
+
} from 'type-graphql'
|
10
|
+
import { In } from 'typeorm'
|
11
|
+
|
12
|
+
import { getEndpointUrl } from '../../utils/config-resolver'
|
13
|
+
import { DataArchive } from './data-archive'
|
14
|
+
import {
|
15
|
+
DataArchivePatch,
|
16
|
+
NewDataArchive
|
17
|
+
} from './data-archive-type'
|
18
|
+
|
19
|
+
const { config } = require('@things-factory/env')
|
20
|
+
const crypto = require('crypto')
|
21
|
+
|
22
|
+
@Resolver(DataArchive)
|
23
|
+
export class DataArchiveMutation {
|
24
|
+
/**
|
25
|
+
* It may use 'func-[dev]-data-set-download' in https://github.com/operatochef/serverless
|
26
|
+
* This function requests athena query and save the result as csv.gz in s3
|
27
|
+
*/
|
28
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
29
|
+
@Directive('@transaction')
|
30
|
+
@Mutation(returns => DataArchive, { description: 'To create new DataArchive' })
|
31
|
+
async generatePresignedUrl(
|
32
|
+
@Arg('patch') patch: DataArchivePatch,
|
33
|
+
@Ctx() context: ResolverContext
|
34
|
+
): Promise<DataArchive> {
|
35
|
+
const { domain, user, tx, notify, lng } = context.state
|
36
|
+
// const dataArchiveRepo = tx.getRepository(DataArchive)
|
37
|
+
const t = i18next.getFixedT(lng, 'translations')
|
38
|
+
const {
|
39
|
+
dataset: {
|
40
|
+
endpoint,
|
41
|
+
datasource: { database }
|
42
|
+
}
|
43
|
+
} = config.get('dataArchive')
|
44
|
+
|
45
|
+
const body = JSON.stringify({
|
46
|
+
domain: domain?.subdomain,
|
47
|
+
database,
|
48
|
+
...patch?.requestParams
|
49
|
+
})
|
50
|
+
|
51
|
+
const url = getEndpointUrl(endpoint)
|
52
|
+
|
53
|
+
const fetched = await fetch(`${url}`, {
|
54
|
+
method: 'POST',
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
56
|
+
body
|
57
|
+
})
|
58
|
+
|
59
|
+
const message = {
|
60
|
+
receivers: [user.email],
|
61
|
+
mode: 'in-app',
|
62
|
+
title: '',
|
63
|
+
body: '',
|
64
|
+
url: ''
|
65
|
+
}
|
66
|
+
|
67
|
+
if (fetched.ok) {
|
68
|
+
const response = await fetched.json()
|
69
|
+
patch.downloadUrl = response['presignedUrl']
|
70
|
+
patch.status = 'done'
|
71
|
+
|
72
|
+
message['title'] = t('title.data-archive downloads ready')
|
73
|
+
message['url'] = patch.downloadUrl
|
74
|
+
} else {
|
75
|
+
const error = await fetched.text()
|
76
|
+
console.log(error)
|
77
|
+
patch.status = 'error'
|
78
|
+
|
79
|
+
message['title'] = t('title.data-archive request failed')
|
80
|
+
// may not user-friendly message
|
81
|
+
message['body'] = error
|
82
|
+
}
|
83
|
+
|
84
|
+
const result = await this.updateDataArchive(patch.id, patch, context)
|
85
|
+
|
86
|
+
notify && notify(message)
|
87
|
+
|
88
|
+
return result
|
89
|
+
}
|
90
|
+
|
91
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
92
|
+
@Directive('@transaction')
|
93
|
+
@Mutation(returns => DataArchive, { description: 'To create new DataArchive' })
|
94
|
+
async createDataArchive(
|
95
|
+
@Arg('dataArchive') dataArchive: NewDataArchive,
|
96
|
+
@Ctx() context: ResolverContext
|
97
|
+
): Promise<DataArchive> {
|
98
|
+
const { domain, user, tx } = context.state
|
99
|
+
const dataArchiveRepo = tx.getRepository(DataArchive)
|
100
|
+
|
101
|
+
const result = await dataArchiveRepo.save({
|
102
|
+
...dataArchive,
|
103
|
+
domain,
|
104
|
+
creator: user,
|
105
|
+
updater: user
|
106
|
+
})
|
107
|
+
|
108
|
+
return result
|
109
|
+
}
|
110
|
+
|
111
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
112
|
+
@Directive('@transaction')
|
113
|
+
@Mutation(returns => DataArchive, { description: 'To modify DataArchive information' })
|
114
|
+
async updateDataArchive(
|
115
|
+
@Arg('id') id: string,
|
116
|
+
@Arg('patch') patch: DataArchivePatch,
|
117
|
+
@Ctx() context: ResolverContext
|
118
|
+
): Promise<DataArchive> {
|
119
|
+
const { domain, user, tx } = context.state
|
120
|
+
const dataArchiveRepo = tx.getRepository(DataArchive)
|
121
|
+
|
122
|
+
const dataArchive = await dataArchiveRepo.findOne({
|
123
|
+
where: { domain: { id: domain.id }, id },
|
124
|
+
relations: ['domain', 'creator', 'updater']
|
125
|
+
})
|
126
|
+
|
127
|
+
const result = await dataArchiveRepo.save({
|
128
|
+
...dataArchive,
|
129
|
+
...patch,
|
130
|
+
updater: user
|
131
|
+
})
|
132
|
+
|
133
|
+
return result
|
134
|
+
}
|
135
|
+
|
136
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
137
|
+
@Directive('@transaction')
|
138
|
+
@Mutation(returns => [DataArchive], { description: "To modify multiple DataArchives' information" })
|
139
|
+
async updateMultipleDataArchive(
|
140
|
+
@Arg('patches', type => [DataArchivePatch]) patches: DataArchivePatch[],
|
141
|
+
@Ctx() context: any
|
142
|
+
): Promise<DataArchive[]> {
|
143
|
+
const { domain, user, tx } = context.state
|
144
|
+
const dataArchiveRepo = tx.getRepository(DataArchive)
|
145
|
+
|
146
|
+
let results = []
|
147
|
+
const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
|
148
|
+
const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
|
149
|
+
|
150
|
+
if (_createRecords.length > 0) {
|
151
|
+
const cuFlag = '+'
|
152
|
+
for (let i = 0; i < _createRecords.length; i++) {
|
153
|
+
const newRecord = _createRecords[i]
|
154
|
+
|
155
|
+
const result = await dataArchiveRepo.save({
|
156
|
+
...newRecord,
|
157
|
+
domain,
|
158
|
+
creator: user,
|
159
|
+
updater: user
|
160
|
+
})
|
161
|
+
|
162
|
+
results.push({
|
163
|
+
...result,
|
164
|
+
cuFlag
|
165
|
+
})
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
if (_updateRecords.length > 0) {
|
170
|
+
const cuFlag = 'M'
|
171
|
+
for (let i = 0; i < _updateRecords.length; i++) {
|
172
|
+
const newRecord = _updateRecords[i]
|
173
|
+
const dataArchive = await dataArchiveRepo.findOne(newRecord.id, {
|
174
|
+
relations: ['domain', 'creator', 'updater']
|
175
|
+
})
|
176
|
+
|
177
|
+
const result = await dataArchiveRepo.save({
|
178
|
+
...dataArchive,
|
179
|
+
...newRecord,
|
180
|
+
updater: user
|
181
|
+
})
|
182
|
+
|
183
|
+
results.push({
|
184
|
+
...result,
|
185
|
+
cuFlag
|
186
|
+
})
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
return results
|
191
|
+
}
|
192
|
+
|
193
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
194
|
+
@Directive('@transaction')
|
195
|
+
@Mutation(returns => Boolean, { description: 'To delete DataArchive' })
|
196
|
+
async deleteDataArchive(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
|
197
|
+
const { domain, tx } = context.state
|
198
|
+
|
199
|
+
await tx.getRepository(DataArchive).delete({ domain, id })
|
200
|
+
return true
|
201
|
+
}
|
202
|
+
|
203
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
204
|
+
@Directive('@transaction')
|
205
|
+
@Mutation(returns => Boolean, { description: 'To delete multiple dataArchives' })
|
206
|
+
async deleteDataArchives(
|
207
|
+
@Arg('ids', type => [String]) ids: string[],
|
208
|
+
@Ctx() context: ResolverContext
|
209
|
+
): Promise<boolean> {
|
210
|
+
const { domain, tx } = context.state
|
211
|
+
|
212
|
+
await tx.getRepository(DataArchive).delete({
|
213
|
+
domain,
|
214
|
+
id: In(ids)
|
215
|
+
})
|
216
|
+
|
217
|
+
return true
|
218
|
+
}
|
219
|
+
|
220
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
221
|
+
@Directive('@transaction')
|
222
|
+
@Mutation(returns => Boolean, { description: 'To import multiple data-archives' })
|
223
|
+
async importDataArchives(
|
224
|
+
@Arg('dataArchives', type => [DataArchivePatch]) dataArchives: DataArchive[],
|
225
|
+
@Ctx() context: ResolverContext
|
226
|
+
): Promise<boolean> {
|
227
|
+
const { domain, tx } = context.state
|
228
|
+
const dataArchiveRepo = tx.getRepository(DataArchive)
|
229
|
+
|
230
|
+
await Promise.all(
|
231
|
+
dataArchives.map(async (dataArchive: DataArchive) => {
|
232
|
+
const createdDataArchive: DataArchive = await dataArchiveRepo.save({
|
233
|
+
domain,
|
234
|
+
...dataArchive
|
235
|
+
})
|
236
|
+
})
|
237
|
+
)
|
238
|
+
|
239
|
+
return true
|
240
|
+
}
|
241
|
+
|
242
|
+
@Directive('@privilege(category: "data-archive", privilege: "mutation", domainOwnerGranted: true)')
|
243
|
+
@Directive('@transaction')
|
244
|
+
@Mutation(returns => [DataArchive], { description: 'To copy multiple data-archives' })
|
245
|
+
async copyDataArchives(
|
246
|
+
@Arg('ids', type => [String]) ids: string[],
|
247
|
+
@Ctx() context: ResolverContext
|
248
|
+
): Promise<DataArchive[]> {
|
249
|
+
const { domain, user, tx } = context.state
|
250
|
+
const dataArchiveRepo = tx.getRepository(DataArchive)
|
251
|
+
|
252
|
+
const originals = await dataArchiveRepo.find({
|
253
|
+
where: {
|
254
|
+
id: In(ids),
|
255
|
+
domain: { id: domain.id }
|
256
|
+
},
|
257
|
+
relations: ['domain']
|
258
|
+
})
|
259
|
+
|
260
|
+
if (originals.length == 0) {
|
261
|
+
return []
|
262
|
+
}
|
263
|
+
|
264
|
+
var newCopys = originals.map(dataArchive => {
|
265
|
+
let dataArchiveId = crypto.randomUUID()
|
266
|
+
|
267
|
+
return {
|
268
|
+
...dataArchive,
|
269
|
+
id: dataArchiveId,
|
270
|
+
domain,
|
271
|
+
creator: user,
|
272
|
+
updater: user,
|
273
|
+
updatedAt: undefined,
|
274
|
+
createdAt: undefined
|
275
|
+
}
|
276
|
+
})
|
277
|
+
|
278
|
+
var copiedDataArchives = await dataArchiveRepo.save(newCopys)
|
279
|
+
|
280
|
+
return copiedDataArchives
|
281
|
+
}
|
282
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
|
2
|
+
|
3
|
+
import { Role, User } from '@things-factory/auth-base'
|
4
|
+
import { Domain, getQueryBuilderFromListParams, ListParam, getRepository } from '@things-factory/shell'
|
5
|
+
|
6
|
+
import { DataArchive } from './data-archive'
|
7
|
+
import { DataArchiveList } from './data-archive-type'
|
8
|
+
|
9
|
+
var parser = require('cron-parser')
|
10
|
+
|
11
|
+
@Resolver(DataArchive)
|
12
|
+
export class DataArchiveQuery {
|
13
|
+
@Directive('@privilege(category: "data-archive", privilege: "query", domainOwnerGranted: true)')
|
14
|
+
@Query(returns => DataArchive, { description: 'To fetch a DataArchive' })
|
15
|
+
async dataArchive(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<DataArchive> {
|
16
|
+
const { domain } = context.state
|
17
|
+
|
18
|
+
return await getRepository(DataArchive).findOne({
|
19
|
+
where: { domain: { id: domain.id }, id }
|
20
|
+
})
|
21
|
+
}
|
22
|
+
|
23
|
+
@Directive('@privilege(category: "data-archive", privilege: "query", domainOwnerGranted: true)')
|
24
|
+
@Query(returns => DataArchiveList, { description: 'To fetch multiple DataArchives' })
|
25
|
+
async dataArchives(@Args() params: ListParam, @Ctx() context: ResolverContext): Promise<DataArchiveList> {
|
26
|
+
const { domain } = context.state
|
27
|
+
|
28
|
+
const queryBuilder = getQueryBuilderFromListParams({
|
29
|
+
repository: getRepository(DataArchive),
|
30
|
+
params,
|
31
|
+
domain,
|
32
|
+
alias: 'dataArchive',
|
33
|
+
searchables: []
|
34
|
+
})
|
35
|
+
|
36
|
+
const [items, total] = await queryBuilder.getManyAndCount()
|
37
|
+
|
38
|
+
return { items, total }
|
39
|
+
}
|
40
|
+
|
41
|
+
@FieldResolver(type => Domain)
|
42
|
+
async domain(@Root() dataArchive: DataArchive): Promise<Domain> {
|
43
|
+
return await getRepository(Domain).findOneBy({ id: dataArchive.domainId })
|
44
|
+
}
|
45
|
+
|
46
|
+
@FieldResolver(type => User)
|
47
|
+
async updater(@Root() dataArchive: DataArchive): Promise<User> {
|
48
|
+
return await getRepository(User).findOneBy({ id: dataArchive.updaterId })
|
49
|
+
}
|
50
|
+
|
51
|
+
@FieldResolver(type => User)
|
52
|
+
async creator(@Root() dataArchive: DataArchive): Promise<User> {
|
53
|
+
return await getRepository(User).findOneBy({ id: dataArchive.creatorId })
|
54
|
+
}
|
55
|
+
}
|