datajunction 0.0.1-rc.0 → 0.0.2
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/.babelrc +7 -7
- package/.eslintrc.js +4 -6
- package/.prettierignore +1 -0
- package/.prettierrc +6 -0
- package/Makefile +3 -0
- package/babel.config.js +1 -0
- package/index.js +1 -1
- package/package.json +17 -7
- package/src/httpclient.js +107 -63
- package/src/index.js +296 -33
- package/src/index.test.js +80 -0
- package/webpack.config.js +3 -3
- package/dist/datajunction.js +0 -1
- package/dist/httpclient.js +0 -120
- package/dist/index.js +0 -113
package/.babelrc
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
2
|
+
"presets": [
|
|
3
|
+
"@babel/preset-env"
|
|
4
|
+
],
|
|
5
|
+
"test": [
|
|
6
|
+
"jest"
|
|
7
|
+
]
|
|
8
|
+
}
|
package/.eslintrc.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
env: {
|
|
3
3
|
browser: true,
|
|
4
|
-
es2021: true
|
|
4
|
+
es2021: true,
|
|
5
5
|
},
|
|
6
6
|
extends: 'standard',
|
|
7
|
-
overrides: [
|
|
8
|
-
],
|
|
7
|
+
overrides: [],
|
|
9
8
|
parserOptions: {
|
|
10
9
|
ecmaVersion: 'latest',
|
|
11
|
-
sourceType: 'module'
|
|
10
|
+
sourceType: 'module',
|
|
12
11
|
},
|
|
13
|
-
rules: {
|
|
14
|
-
}
|
|
12
|
+
rules: {},
|
|
15
13
|
}
|
package/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
./dist
|
package/.prettierrc
ADDED
package/Makefile
ADDED
package/babel.config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {presets: ['@babel/preset-env']}
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = require('./dist')
|
|
1
|
+
module.exports = require('./dist')
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datajunction",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "A Javascript client for interacting with a DataJunction server",
|
|
5
5
|
"module": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test-watch": "jest --watch",
|
|
8
9
|
"build": "rm -rf dist/* && webpack && babel src -d dist",
|
|
9
|
-
"lint": "
|
|
10
|
-
"
|
|
10
|
+
"lint": "prettier \"src/**/*.{js,jsx}\"",
|
|
11
|
+
"format": "prettier --write \"src/**/*.{js,jsx}\""
|
|
11
12
|
},
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
@@ -26,15 +27,24 @@
|
|
|
26
27
|
},
|
|
27
28
|
"homepage": "https://github.com/DataJunction/dj#readme",
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"babel
|
|
30
|
-
"babel
|
|
31
|
-
"babel-
|
|
30
|
+
"@babel/cli": "^7.0.0",
|
|
31
|
+
"@babel/core": "^7.0.0",
|
|
32
|
+
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
|
|
33
|
+
"@babel/preset-env": "^7.0.0",
|
|
34
|
+
"babel-core": "^7.0.0-bridge.0",
|
|
35
|
+
"babel-jest": "^23.4.2",
|
|
32
36
|
"eslint": "^8.39.0",
|
|
33
37
|
"eslint-config-standard": "^17.0.0",
|
|
34
38
|
"eslint-plugin-import": "^2.27.5",
|
|
35
39
|
"eslint-plugin-n": "^15.7.0",
|
|
36
40
|
"eslint-plugin-promise": "^6.1.1",
|
|
41
|
+
"jest": "^29.5.0",
|
|
42
|
+
"prettier": "^2.8.8",
|
|
37
43
|
"webpack": "^5.81.0",
|
|
38
44
|
"webpack-cli": "^5.0.2"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@babel/core": "^7.22.5",
|
|
48
|
+
"docker-names": "^1.2.1"
|
|
39
49
|
}
|
|
40
50
|
}
|
package/src/httpclient.js
CHANGED
|
@@ -1,78 +1,122 @@
|
|
|
1
1
|
export default class HttpClient {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this._baseURL = options.baseURL || '';
|
|
4
|
+
this._headers = options.headers || {};
|
|
5
|
+
this._cookie = '';
|
|
6
|
+
}
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
async _fetchJSON(endpoint, options = {}) {
|
|
9
|
+
const res = await fetch(this._baseURL + endpoint, {
|
|
10
|
+
...options,
|
|
11
|
+
headers: {
|
|
12
|
+
...this._headers,
|
|
13
|
+
'Cookie': this._cookie,
|
|
14
|
+
},
|
|
15
|
+
credentials: 'include',
|
|
16
|
+
});
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
const setCookieHeader = res.headers.get('Set-Cookie');
|
|
19
|
+
if (setCookieHeader) {
|
|
20
|
+
this._cookie = setCookieHeader;
|
|
21
|
+
this._headers['Cookie'] = this._cookie;
|
|
22
|
+
}
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const errorText = await res.text();
|
|
26
|
+
throw new Error(`Request failed: ${res.status} ${errorText}`);
|
|
27
|
+
}
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
if (options.parseResponse !== false && res.status !== 204) {
|
|
30
|
+
return res.json();
|
|
31
|
+
}
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return this
|
|
23
|
-
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
async login(username, password) {
|
|
37
|
+
const body = new URLSearchParams({
|
|
38
|
+
grant_type: 'password',
|
|
39
|
+
username: username,
|
|
40
|
+
password: password,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const response = await fetch(this._baseURL + '/basic/login', {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
47
|
+
...this._headers,
|
|
48
|
+
},
|
|
49
|
+
body: body.toString(),
|
|
50
|
+
credentials: 'include',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const errorText = await response.text();
|
|
55
|
+
throw new Error(`Login failed: ${response.status} ${errorText}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const setCookieHeader = response.headers.get('Set-Cookie');
|
|
59
|
+
if (setCookieHeader) {
|
|
60
|
+
this._cookie = setCookieHeader;
|
|
61
|
+
this._headers['Cookie'] = this._cookie;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
setHeader(key, value) {
|
|
65
|
+
this._headers[key] = value
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
28
68
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
69
|
+
getHeader(key) {
|
|
70
|
+
return this._headers[key]
|
|
71
|
+
}
|
|
33
72
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
73
|
+
setBasicAuth(username, password) {
|
|
74
|
+
this._headers.Authorization = `Basic ${btoa(`${username}:${password}`)}`
|
|
75
|
+
return this
|
|
76
|
+
}
|
|
38
77
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
})
|
|
44
|
-
}
|
|
78
|
+
setBearerAuth(token) {
|
|
79
|
+
this._headers.Authorization = `Bearer ${token}`
|
|
80
|
+
return this
|
|
81
|
+
}
|
|
45
82
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
}
|
|
83
|
+
async get(endpoint, options = {}) {
|
|
84
|
+
return this._fetchJSON(endpoint, {
|
|
85
|
+
...options,
|
|
86
|
+
method: 'GET',
|
|
87
|
+
})
|
|
88
|
+
}
|
|
53
89
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
async post(endpoint, body, options = {}) {
|
|
91
|
+
return this._fetchJSON(endpoint, {
|
|
92
|
+
...options,
|
|
93
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
94
|
+
method: 'POST',
|
|
95
|
+
})
|
|
96
|
+
}
|
|
61
97
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
}
|
|
98
|
+
async put(endpoint, body, options = {}) {
|
|
99
|
+
return this._fetchJSON(endpoint, {
|
|
100
|
+
...options,
|
|
101
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
102
|
+
method: 'PUT',
|
|
103
|
+
})
|
|
104
|
+
}
|
|
70
105
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
async patch(endpoint, operations, options = {}) {
|
|
107
|
+
return this._fetchJSON(endpoint, {
|
|
108
|
+
parseResponse: false,
|
|
109
|
+
...options,
|
|
110
|
+
body: JSON.stringify(operations),
|
|
111
|
+
method: 'PATCH',
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async delete(endpoint, options = {}) {
|
|
116
|
+
return this._fetchJSON(endpoint, {
|
|
117
|
+
parseResponse: false,
|
|
118
|
+
...options,
|
|
119
|
+
method: 'DELETE',
|
|
120
|
+
})
|
|
121
|
+
}
|
|
78
122
|
}
|
package/src/index.js
CHANGED
|
@@ -1,51 +1,314 @@
|
|
|
1
1
|
import HttpClient from './httpclient.js'
|
|
2
2
|
|
|
3
3
|
export class DJClient extends HttpClient {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
constructor(
|
|
5
|
+
baseURL,
|
|
6
|
+
namespace,
|
|
7
|
+
engineName = null,
|
|
8
|
+
engineVersion = null,
|
|
9
|
+
httpAgent = null,
|
|
10
|
+
) {
|
|
11
|
+
super(
|
|
12
|
+
{
|
|
13
|
+
baseURL,
|
|
14
|
+
},
|
|
15
|
+
httpAgent
|
|
16
|
+
)
|
|
17
|
+
this.namespace = namespace
|
|
18
|
+
this.engineName = engineName
|
|
19
|
+
this.engineVersion = engineVersion
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get healthcheck() {
|
|
23
|
+
return {
|
|
24
|
+
get: () => this.get('/health/'),
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get catalogs() {
|
|
29
|
+
return {
|
|
30
|
+
list: () => this.get('/catalogs/'),
|
|
31
|
+
get: (catalog) => this.get(`/catalogs/${catalog}/`),
|
|
32
|
+
create: (catalog) =>
|
|
33
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
34
|
+
`/catalogs/`,
|
|
35
|
+
catalog
|
|
36
|
+
),
|
|
37
|
+
addEngine: (catalog, engineName, engineVersion) =>
|
|
38
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
39
|
+
`/catalogs/${catalog}/engines/`,
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
name: engineName,
|
|
43
|
+
version: engineVersion,
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get engines() {
|
|
51
|
+
return {
|
|
52
|
+
list: () => this.get('/engines/'),
|
|
53
|
+
get: (engineName, engineVersion) =>
|
|
54
|
+
this.get(`/engines/${engineName}/${engineVersion}/`),
|
|
55
|
+
create: (engine) => this.post('/engines/', engine),
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get addEngineToCatalog() {
|
|
60
|
+
return {
|
|
61
|
+
set: (catalogName, engine) =>
|
|
62
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
63
|
+
`/catalogs/${catalogName}/engines/`,
|
|
64
|
+
[engine]
|
|
65
|
+
),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get namespaces() {
|
|
70
|
+
return {
|
|
71
|
+
list: () => this.get('/namespaces/'),
|
|
72
|
+
nodes: (namespace) => this.get(`/namespaces/${namespace}/`),
|
|
73
|
+
create: (namespace) =>
|
|
74
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
75
|
+
`/namespaces/${namespace}/`
|
|
76
|
+
),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get commonDimensions() {
|
|
81
|
+
return {
|
|
82
|
+
list: (metrics) => {
|
|
83
|
+
const metricsQuery =
|
|
84
|
+
'?' + metrics.map((m) => `metric=${m}`).join('&')
|
|
85
|
+
return this.get('/metrics/common/dimensions/' + metricsQuery)
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get nodes() {
|
|
91
|
+
return {
|
|
92
|
+
get: (nodeName) => this.get(`/nodes/${nodeName}/`),
|
|
93
|
+
validate: (nodeDetails) =>
|
|
94
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
95
|
+
'/nodes/validate/',
|
|
96
|
+
nodeDetails
|
|
97
|
+
),
|
|
98
|
+
update: (nodeName, nodeDetails) =>
|
|
99
|
+
this.setHeader('Content-Type', 'application/json').patch(
|
|
100
|
+
`/nodes/${nodeName}/`,
|
|
101
|
+
nodeDetails
|
|
102
|
+
),
|
|
103
|
+
revisions: (nodeName) => this.get(`/nodes/${nodeName}/revisions/`),
|
|
104
|
+
downstream: (nodeName) =>
|
|
105
|
+
this.get(`/nodes/${nodeName}/downstream/`),
|
|
106
|
+
upstream: (nodeName) => this.get(`/nodes/${nodeName}/upstream/`),
|
|
107
|
+
publish: (nodeName) => this.patch(`/nodes/${nodeName}/`, {'mode': 'published'})
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get sources() {
|
|
112
|
+
return {
|
|
113
|
+
create: (sourceDetails) =>
|
|
114
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
115
|
+
'/nodes/source/',
|
|
116
|
+
sourceDetails
|
|
117
|
+
),
|
|
118
|
+
list: () => this.get(`/namespaces/${this.namespace}/?type_=source`),
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get transforms() {
|
|
123
|
+
return {
|
|
124
|
+
create: (transformDetails) =>
|
|
125
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
126
|
+
'/nodes/transform/',
|
|
127
|
+
transformDetails
|
|
128
|
+
),
|
|
129
|
+
list: () =>
|
|
130
|
+
this.get(`/namespaces/${this.namespace}/?type_=transform`),
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get dimensions() {
|
|
135
|
+
return {
|
|
136
|
+
create: (dimensionDetails) =>
|
|
137
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
138
|
+
'/nodes/dimension/',
|
|
139
|
+
dimensionDetails
|
|
140
|
+
),
|
|
141
|
+
list: () =>
|
|
142
|
+
this.get(`/namespaces/${this.namespace}/?type_=dimension`),
|
|
143
|
+
link: (nodeName, nodeColumn, dimension, dimensionColumn) =>
|
|
144
|
+
this.post(
|
|
145
|
+
`/nodes/${nodeName}/columns/${nodeColumn}/?dimension=${dimension}&dimension_column=${dimensionColumn}`
|
|
146
|
+
),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get metrics() {
|
|
151
|
+
return {
|
|
152
|
+
get: (metricName) => this.get(`/metrics/${metricName}/`),
|
|
153
|
+
create: (metricDetails) =>
|
|
154
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
155
|
+
'/nodes/metric/',
|
|
156
|
+
metricDetails
|
|
157
|
+
),
|
|
158
|
+
list: () => this.get(`/namespaces/${this.namespace}/?type_=metric`),
|
|
159
|
+
all: () => this.get(`/metrics/`),
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get cubes() {
|
|
164
|
+
return {
|
|
165
|
+
get: (cubeName) => this.get(`/cubes/${cubeName}/`),
|
|
166
|
+
create: (cubeDetails) =>
|
|
167
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
168
|
+
'/nodes/cube/',
|
|
169
|
+
cubeDetails
|
|
170
|
+
),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get tags() {
|
|
175
|
+
return {
|
|
176
|
+
list: () => this.get('/tags/'),
|
|
177
|
+
get: (tagName) => this.get(`/tags/${tagName}/`),
|
|
178
|
+
create: (tagData) =>
|
|
179
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
180
|
+
'/tags/',
|
|
181
|
+
tagData
|
|
182
|
+
),
|
|
183
|
+
update: (tagName, tagData) =>
|
|
184
|
+
this.setHeader('Content-Type', 'application/json').patch(
|
|
185
|
+
`/tags/${tagName}/`,
|
|
186
|
+
tagData
|
|
187
|
+
),
|
|
188
|
+
set: (nodeName, tagName) =>
|
|
189
|
+
this.post(`/nodes/${nodeName}/tag/?tag_name=${tagName}`),
|
|
190
|
+
listNodes: (tagName) => this.get(`/tags/${tagName}/nodes/`),
|
|
191
|
+
}
|
|
192
|
+
}
|
|
9
193
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
194
|
+
get attributes() {
|
|
195
|
+
return {
|
|
196
|
+
list: () => this.get('/attributes/'),
|
|
197
|
+
create: (attributeData) =>
|
|
198
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
199
|
+
'/attributes/',
|
|
200
|
+
attributeData
|
|
201
|
+
),
|
|
202
|
+
}
|
|
13
203
|
}
|
|
14
|
-
}
|
|
15
204
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
205
|
+
get materializationConfigs() {
|
|
206
|
+
return {
|
|
207
|
+
update: (nodeName, materializationDetails) =>
|
|
208
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
209
|
+
`/nodes/${nodeName}/materialization/`,
|
|
210
|
+
materializationDetails
|
|
211
|
+
),
|
|
212
|
+
}
|
|
19
213
|
}
|
|
20
|
-
}
|
|
21
214
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
215
|
+
get columnAttributes() {
|
|
216
|
+
return {
|
|
217
|
+
set: (nodeName, columnAttribute) =>
|
|
218
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
219
|
+
`/nodes/${nodeName}/attributes/`,
|
|
220
|
+
[columnAttribute]
|
|
221
|
+
),
|
|
222
|
+
}
|
|
25
223
|
}
|
|
26
|
-
}
|
|
27
224
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
225
|
+
get availabilityState() {
|
|
226
|
+
return {
|
|
227
|
+
set: (nodeName, availabilityState) =>
|
|
228
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
229
|
+
`/data/${nodeName}/availability/`,
|
|
230
|
+
availabilityState
|
|
231
|
+
),
|
|
232
|
+
}
|
|
31
233
|
}
|
|
32
|
-
}
|
|
33
234
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
235
|
+
get sql() {
|
|
236
|
+
return {
|
|
237
|
+
get: (
|
|
238
|
+
metrics,
|
|
239
|
+
dimensions,
|
|
240
|
+
filters,
|
|
241
|
+
engineName = null,
|
|
242
|
+
engineVersion = null
|
|
243
|
+
) => {
|
|
244
|
+
const metricsQuery =
|
|
245
|
+
'?' + metrics.map((m) => `metrics=${m}`).join('&')
|
|
246
|
+
const dimensionsQuery = dimensions
|
|
247
|
+
.map((d) => `dimensions=${d}`)
|
|
248
|
+
.join('&')
|
|
249
|
+
const filtersQuery = filters
|
|
250
|
+
.map((f) => `filters=${f}`)
|
|
251
|
+
.join('&')
|
|
252
|
+
const engineNameP = engineName ? `&engine=${engineName}` : ''
|
|
253
|
+
const engineVersionP = engineVersion
|
|
254
|
+
? `&engine_version=${engineVersion}`
|
|
255
|
+
: ''
|
|
256
|
+
return this.get(
|
|
257
|
+
`/sql/${metricsQuery}&${dimensionsQuery}${filtersQuery}${engineNameP}${engineVersionP}`
|
|
258
|
+
)
|
|
259
|
+
},
|
|
260
|
+
}
|
|
37
261
|
}
|
|
38
|
-
}
|
|
39
262
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
263
|
+
get data() {
|
|
264
|
+
return {
|
|
265
|
+
get: (
|
|
266
|
+
metrics,
|
|
267
|
+
dimensions,
|
|
268
|
+
filters,
|
|
269
|
+
async_ = false,
|
|
270
|
+
engineName = null,
|
|
271
|
+
engineVersion = null
|
|
272
|
+
) => {
|
|
273
|
+
const metricsQuery =
|
|
274
|
+
'?' + metrics.map((m) => `metrics=${m}`).join('&')
|
|
275
|
+
const dimensionsQuery = dimensions
|
|
276
|
+
.map((d) => `dimensions=${d}`)
|
|
277
|
+
.join('&')
|
|
278
|
+
const filtersQuery = filters
|
|
279
|
+
.map((f) => `filters=${f}`)
|
|
280
|
+
.join('&')
|
|
281
|
+
const asyncP = async_ ? `&async_=${async_}` : ''
|
|
282
|
+
const engineNameP = engineName ? `&engine=${engineName}` : ''
|
|
283
|
+
const engineVersionP = engineVersion
|
|
284
|
+
? `&engine_version=${engineVersion}`
|
|
285
|
+
: ''
|
|
286
|
+
const data = this.get(
|
|
287
|
+
`/data/${metricsQuery}&${dimensionsQuery}${filtersQuery}${asyncP}${engineNameP}${engineVersionP}`
|
|
288
|
+
).then((data) => {
|
|
289
|
+
return {
|
|
290
|
+
columns: data.results[0].columns,
|
|
291
|
+
data: data.results[0].rows,
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
return data
|
|
295
|
+
},
|
|
296
|
+
}
|
|
43
297
|
}
|
|
44
|
-
}
|
|
45
298
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
299
|
+
get register() {
|
|
300
|
+
return {
|
|
301
|
+
table: (catalog, schema, table) =>
|
|
302
|
+
this.setHeader('Content-Type', 'application/json').post(
|
|
303
|
+
`/register/table/${catalog}/${schema}/${table}`
|
|
304
|
+
),
|
|
305
|
+
view: (catalog, schema, view, query, replace = false) => {
|
|
306
|
+
const replaceQuery = replace ? `?replace=${replace}` : '';
|
|
307
|
+
return this.setHeader('Content-Type', 'application/json').post(
|
|
308
|
+
`/register/view/${catalog}/${schema}/${view}${replaceQuery}`,
|
|
309
|
+
{ query }
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
49
313
|
}
|
|
50
|
-
}
|
|
51
314
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const { DJClient } = require('./index')
|
|
2
|
+
var dockerNames = require('docker-names')
|
|
3
|
+
|
|
4
|
+
test('should return something', async () => {
|
|
5
|
+
const dj = new DJClient('http://localhost:8000', 'integration.tests', 'dj', 'dj');
|
|
6
|
+
await dj.login("dj", "dj")
|
|
7
|
+
|
|
8
|
+
await dj.catalogs.create({ name: 'tpch' });
|
|
9
|
+
await dj.engines.create({ name: 'trino', version: '451' });
|
|
10
|
+
await dj.catalogs.addEngine('tpch', 'trino', '451');
|
|
11
|
+
|
|
12
|
+
await dj.namespaces.create('integration.tests');
|
|
13
|
+
await dj.namespaces.create('integration.tests.trino');
|
|
14
|
+
|
|
15
|
+
const source = await dj.sources.create({
|
|
16
|
+
name: 'integration.tests.source1',
|
|
17
|
+
catalog: 'unknown',
|
|
18
|
+
schema_: 'db',
|
|
19
|
+
table: 'tbl',
|
|
20
|
+
display_name: 'Test Source with Columns',
|
|
21
|
+
description: 'A test source node with columns',
|
|
22
|
+
columns: [
|
|
23
|
+
{ name: 'id', type: 'int' },
|
|
24
|
+
{ name: 'name', type: 'string' },
|
|
25
|
+
{ name: 'price', type: 'double' },
|
|
26
|
+
{ name: 'created_at', type: 'timestamp' },
|
|
27
|
+
],
|
|
28
|
+
primary_key: ['id'],
|
|
29
|
+
mode: 'published',
|
|
30
|
+
update_if_exists: true,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await dj.register.table('tpch', 'sf1', 'orders');
|
|
34
|
+
|
|
35
|
+
const transform = await dj.transforms.create({
|
|
36
|
+
name: 'integration.tests.trino.transform1',
|
|
37
|
+
display_name: 'Filter to last 1000 records',
|
|
38
|
+
description: 'The last 1000 purchases',
|
|
39
|
+
mode: 'published',
|
|
40
|
+
query: 'select custkey, totalprice, orderdate from source.tpch.sf1.orders order by orderdate desc limit 1000',
|
|
41
|
+
update_if_exists: true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const dimension = await dj.dimensions.create({
|
|
45
|
+
name: 'integration.tests.trino.dimension1',
|
|
46
|
+
display_name: 'Customer keys',
|
|
47
|
+
description: 'All custkey values in the source table',
|
|
48
|
+
mode: 'published',
|
|
49
|
+
primary_key: ['id'],
|
|
50
|
+
tags: [],
|
|
51
|
+
query: "select custkey as id, 'attribute' as foo from source.tpch.sf1.orders",
|
|
52
|
+
update_if_exists: true,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await dj.dimensions.link(
|
|
56
|
+
'integration.tests.trino.transform1',
|
|
57
|
+
'custkey',
|
|
58
|
+
'integration.tests.trino.dimension1',
|
|
59
|
+
'id',
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const metric = await dj.metrics.create({
|
|
63
|
+
name: 'integration.tests.trino.metric1',
|
|
64
|
+
display_name: 'Total of last 1000 purchases',
|
|
65
|
+
description: 'This is the total amount from the last 1000 purchases',
|
|
66
|
+
mode: 'published',
|
|
67
|
+
query: 'select sum(totalprice) from integration.tests.trino.transform1',
|
|
68
|
+
update_if_exists: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await dj.commonDimensions.list(['integration.tests.trino.metric1']);
|
|
72
|
+
|
|
73
|
+
const query = await dj.sql.get(
|
|
74
|
+
['integration.tests.trino.metric1'],
|
|
75
|
+
['integration.tests.trino.dimension1.id'],
|
|
76
|
+
[]
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(query.sql).toContain('SELECT');
|
|
80
|
+
}, 60000)
|
package/webpack.config.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const path = require('path')
|
|
1
|
+
const path = require('path')
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
entry: './src/index.js',
|
|
5
|
-
mode:
|
|
5
|
+
mode: 'production',
|
|
6
6
|
output: {
|
|
7
7
|
path: path.resolve(__dirname, 'dist'),
|
|
8
8
|
filename: 'datajunction.js',
|
|
@@ -12,4 +12,4 @@ module.exports = {
|
|
|
12
12
|
type: 'umd',
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
|
-
}
|
|
15
|
+
}
|
package/dist/datajunction.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.datajunction=t():e.datajunction=t()}(this,(()=>(()=>{"use strict";var e={d:(t,s)=>{for(var r in s)e.o(s,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:s[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{DJClient:()=>r});class s{constructor(e={}){this._baseURL=e.baseURL||"",this._headers=e.headers||{}}async _fetchJSON(e,t={}){const s=await fetch(this._baseURL+e,{...t,headers:this._headers});if(!s.ok)throw new Error(s.statusText);if(!1!==t.parseResponse&&204!==s.status)return s.json()}setHeader(e,t){return this._headers[e]=t,this}getHeader(e){return this._headers[e]}setBasicAuth(e,t){return this._headers.Authorization=`Basic ${btoa(`${e}:${t}`)}`,this}setBearerAuth(e){return this._headers.Authorization=`Bearer ${e}`,this}async get(e,t={}){return this._fetchJSON(e,{...t,method:"GET"})}async post(e,t,s={}){return this._fetchJSON(e,{...s,body:t?JSON.stringify(t):void 0,method:"POST"})}async put(e,t,s={}){return this._fetchJSON(e,{...s,body:t?JSON.stringify(t):void 0,method:"PUT"})}async patch(e,t,s={}){return this._fetchJSON(e,{parseResponse:!1,...s,body:JSON.stringify(t),method:"PATCH"})}async delete(e,t={}){return this._fetchJSON(e,{parseResponse:!1,...t,method:"DELETE"})}}class r extends s{constructor(e){super({baseURL:e})}get catalogs(){return{get:()=>this.get("/catalogs/")}}get engines(){return{get:()=>this.get("/engines/")}}get namespaces(){return{get:()=>this.get("/namespaces/")}}get metrics(){return{get:()=>this.get("/metrics/")}}get nodes(){return{get:()=>this.get("/nodes/")}}get tags(){return{get:()=>this.get("/tags/")}}get attributes(){return{get:()=>this.get("/attributes/")}}}return t})()));
|
package/dist/httpclient.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
8
|
-
|
|
9
|
-
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
10
|
-
|
|
11
|
-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
12
|
-
|
|
13
|
-
var HttpClient = function () {
|
|
14
|
-
function HttpClient() {
|
|
15
|
-
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
16
|
-
|
|
17
|
-
_classCallCheck(this, HttpClient);
|
|
18
|
-
|
|
19
|
-
this._baseURL = options.baseURL || '';
|
|
20
|
-
this._headers = options.headers || {};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
_createClass(HttpClient, [{
|
|
24
|
-
key: '_fetchJSON',
|
|
25
|
-
value: async function _fetchJSON(endpoint) {
|
|
26
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
27
|
-
|
|
28
|
-
var res = await fetch(this._baseURL + endpoint, _extends({}, options, {
|
|
29
|
-
headers: this._headers
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
if (!res.ok) throw new Error(res.statusText);
|
|
33
|
-
|
|
34
|
-
if (options.parseResponse !== false && res.status !== 204) {
|
|
35
|
-
return res.json();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}, {
|
|
41
|
-
key: 'setHeader',
|
|
42
|
-
value: function setHeader(key, value) {
|
|
43
|
-
this._headers[key] = value;
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
}, {
|
|
47
|
-
key: 'getHeader',
|
|
48
|
-
value: function getHeader(key) {
|
|
49
|
-
return this._headers[key];
|
|
50
|
-
}
|
|
51
|
-
}, {
|
|
52
|
-
key: 'setBasicAuth',
|
|
53
|
-
value: function setBasicAuth(username, password) {
|
|
54
|
-
this._headers.Authorization = 'Basic ' + btoa(username + ':' + password);
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
}, {
|
|
58
|
-
key: 'setBearerAuth',
|
|
59
|
-
value: function setBearerAuth(token) {
|
|
60
|
-
this._headers.Authorization = 'Bearer ' + token;
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
}, {
|
|
64
|
-
key: 'get',
|
|
65
|
-
value: async function get(endpoint) {
|
|
66
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
67
|
-
|
|
68
|
-
return this._fetchJSON(endpoint, _extends({}, options, {
|
|
69
|
-
method: 'GET'
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
|
-
}, {
|
|
73
|
-
key: 'post',
|
|
74
|
-
value: async function post(endpoint, body) {
|
|
75
|
-
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
76
|
-
|
|
77
|
-
return this._fetchJSON(endpoint, _extends({}, options, {
|
|
78
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
79
|
-
method: 'POST'
|
|
80
|
-
}));
|
|
81
|
-
}
|
|
82
|
-
}, {
|
|
83
|
-
key: 'put',
|
|
84
|
-
value: async function put(endpoint, body) {
|
|
85
|
-
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
86
|
-
|
|
87
|
-
return this._fetchJSON(endpoint, _extends({}, options, {
|
|
88
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
89
|
-
method: 'PUT'
|
|
90
|
-
}));
|
|
91
|
-
}
|
|
92
|
-
}, {
|
|
93
|
-
key: 'patch',
|
|
94
|
-
value: async function patch(endpoint, operations) {
|
|
95
|
-
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
96
|
-
|
|
97
|
-
return this._fetchJSON(endpoint, _extends({
|
|
98
|
-
parseResponse: false
|
|
99
|
-
}, options, {
|
|
100
|
-
body: JSON.stringify(operations),
|
|
101
|
-
method: 'PATCH'
|
|
102
|
-
}));
|
|
103
|
-
}
|
|
104
|
-
}, {
|
|
105
|
-
key: 'delete',
|
|
106
|
-
value: async function _delete(endpoint) {
|
|
107
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
108
|
-
|
|
109
|
-
return this._fetchJSON(endpoint, _extends({
|
|
110
|
-
parseResponse: false
|
|
111
|
-
}, options, {
|
|
112
|
-
method: 'DELETE'
|
|
113
|
-
}));
|
|
114
|
-
}
|
|
115
|
-
}]);
|
|
116
|
-
|
|
117
|
-
return HttpClient;
|
|
118
|
-
}();
|
|
119
|
-
|
|
120
|
-
exports.default = HttpClient;
|
package/dist/index.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.DJClient = undefined;
|
|
7
|
-
|
|
8
|
-
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
9
|
-
|
|
10
|
-
var _httpclient = require('./httpclient.js');
|
|
11
|
-
|
|
12
|
-
var _httpclient2 = _interopRequireDefault(_httpclient);
|
|
13
|
-
|
|
14
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
|
|
16
|
-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
17
|
-
|
|
18
|
-
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
19
|
-
|
|
20
|
-
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
21
|
-
|
|
22
|
-
var DJClient = exports.DJClient = function (_HttpClient) {
|
|
23
|
-
_inherits(DJClient, _HttpClient);
|
|
24
|
-
|
|
25
|
-
function DJClient(baseURL) {
|
|
26
|
-
_classCallCheck(this, DJClient);
|
|
27
|
-
|
|
28
|
-
return _possibleConstructorReturn(this, (DJClient.__proto__ || Object.getPrototypeOf(DJClient)).call(this, {
|
|
29
|
-
baseURL: baseURL
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
_createClass(DJClient, [{
|
|
34
|
-
key: 'catalogs',
|
|
35
|
-
get: function get() {
|
|
36
|
-
var _this2 = this;
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
get: function get() {
|
|
40
|
-
return _this2.get('/catalogs/');
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}, {
|
|
45
|
-
key: 'engines',
|
|
46
|
-
get: function get() {
|
|
47
|
-
var _this3 = this;
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
get: function get() {
|
|
51
|
-
return _this3.get('/engines/');
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}, {
|
|
56
|
-
key: 'namespaces',
|
|
57
|
-
get: function get() {
|
|
58
|
-
var _this4 = this;
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
get: function get() {
|
|
62
|
-
return _this4.get('/namespaces/');
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}, {
|
|
67
|
-
key: 'metrics',
|
|
68
|
-
get: function get() {
|
|
69
|
-
var _this5 = this;
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
get: function get() {
|
|
73
|
-
return _this5.get('/metrics/');
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}, {
|
|
78
|
-
key: 'nodes',
|
|
79
|
-
get: function get() {
|
|
80
|
-
var _this6 = this;
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
get: function get() {
|
|
84
|
-
return _this6.get('/nodes/');
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}, {
|
|
89
|
-
key: 'tags',
|
|
90
|
-
get: function get() {
|
|
91
|
-
var _this7 = this;
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
get: function get() {
|
|
95
|
-
return _this7.get('/tags/');
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}, {
|
|
100
|
-
key: 'attributes',
|
|
101
|
-
get: function get() {
|
|
102
|
-
var _this8 = this;
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
get: function get() {
|
|
106
|
-
return _this8.get('/attributes/');
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}]);
|
|
111
|
-
|
|
112
|
-
return DJClient;
|
|
113
|
-
}(_httpclient2.default);
|