datajunction 0.0.1-a100
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 +8 -0
- package/.eslintignore +1 -0
- package/.eslintrc.js +13 -0
- package/.prettierignore +1 -0
- package/.prettierrc +6 -0
- package/Makefile +3 -0
- package/babel.config.js +1 -0
- package/index.js +1 -0
- package/package.json +50 -0
- package/src/httpclient.js +122 -0
- package/src/index.js +314 -0
- package/src/index.test.js +80 -0
- package/webpack.config.js +15 -0
package/.babelrc
ADDED
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
./dist/*
|
package/.eslintrc.js
ADDED
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
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist')
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "datajunction",
|
|
3
|
+
"version": "0.0.1a100",
|
|
4
|
+
"description": "A Javascript client for interacting with a DataJunction server",
|
|
5
|
+
"module": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test-watch": "jest --watch",
|
|
9
|
+
"build": "rm -rf dist/* && webpack && babel src -d dist",
|
|
10
|
+
"lint": "prettier \"src/**/*.{js,jsx}\"",
|
|
11
|
+
"format": "prettier --write \"src/**/*.{js,jsx}\""
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/DataJunction/dj.git"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"datajunction",
|
|
19
|
+
"metrics",
|
|
20
|
+
"metrics-platform",
|
|
21
|
+
"semantic-layer"
|
|
22
|
+
],
|
|
23
|
+
"author": "DataJunction Authors",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/DataJunction/dj/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/DataJunction/dj#readme",
|
|
29
|
+
"devDependencies": {
|
|
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",
|
|
36
|
+
"eslint": "^8.39.0",
|
|
37
|
+
"eslint-config-standard": "^17.0.0",
|
|
38
|
+
"eslint-plugin-import": "^2.27.5",
|
|
39
|
+
"eslint-plugin-n": "^15.7.0",
|
|
40
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
41
|
+
"jest": "^29.5.0",
|
|
42
|
+
"prettier": "^2.8.8",
|
|
43
|
+
"webpack": "^5.81.0",
|
|
44
|
+
"webpack-cli": "^5.0.2"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@babel/core": "^7.22.5",
|
|
48
|
+
"docker-names": "^1.2.1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export default class HttpClient {
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this._baseURL = options.baseURL || '';
|
|
4
|
+
this._headers = options.headers || {};
|
|
5
|
+
this._cookie = '';
|
|
6
|
+
}
|
|
7
|
+
|
|
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
|
+
});
|
|
17
|
+
|
|
18
|
+
const setCookieHeader = res.headers.get('Set-Cookie');
|
|
19
|
+
if (setCookieHeader) {
|
|
20
|
+
this._cookie = setCookieHeader;
|
|
21
|
+
this._headers['Cookie'] = this._cookie;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const errorText = await res.text();
|
|
26
|
+
throw new Error(`Request failed: ${res.status} ${errorText}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (options.parseResponse !== false && res.status !== 204) {
|
|
30
|
+
return res.json();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
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
|
+
}
|
|
68
|
+
|
|
69
|
+
getHeader(key) {
|
|
70
|
+
return this._headers[key]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setBasicAuth(username, password) {
|
|
74
|
+
this._headers.Authorization = `Basic ${btoa(`${username}:${password}`)}`
|
|
75
|
+
return this
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setBearerAuth(token) {
|
|
79
|
+
this._headers.Authorization = `Bearer ${token}`
|
|
80
|
+
return this
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async get(endpoint, options = {}) {
|
|
84
|
+
return this._fetchJSON(endpoint, {
|
|
85
|
+
...options,
|
|
86
|
+
method: 'GET',
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
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
|
+
}
|
|
97
|
+
|
|
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
|
+
}
|
|
105
|
+
|
|
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
|
+
}
|
|
122
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import HttpClient from './httpclient.js'
|
|
2
|
+
|
|
3
|
+
export class DJClient extends HttpClient {
|
|
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
|
+
}
|
|
193
|
+
|
|
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
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
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
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
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
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
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
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
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
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
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
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
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
|
+
}
|
|
313
|
+
}
|
|
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)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
entry: './src/index.js',
|
|
5
|
+
mode: 'production',
|
|
6
|
+
output: {
|
|
7
|
+
path: path.resolve(__dirname, 'dist'),
|
|
8
|
+
filename: 'datajunction.js',
|
|
9
|
+
globalObject: 'this',
|
|
10
|
+
library: {
|
|
11
|
+
name: 'datajunction',
|
|
12
|
+
type: 'umd',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
}
|