datajunction 0.0.1-a30
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 +83 -0
- package/src/index.js +298 -0
- package/src/index.test.js +592 -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.1a30",
|
|
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,83 @@
|
|
|
1
|
+
export default class HttpClient {
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this._baseURL = options.baseURL || ''
|
|
4
|
+
this._headers = options.headers || {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async _fetchJSON(endpoint, options = {}) {
|
|
8
|
+
const res = await fetch(this._baseURL + endpoint, {
|
|
9
|
+
...options,
|
|
10
|
+
headers: this._headers,
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
if (!res.ok)
|
|
14
|
+
throw new Error(
|
|
15
|
+
res.statusText + ` (${endpoint}, ${JSON.stringify(options)})`
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if (options.parseResponse !== false && res.status !== 204) {
|
|
19
|
+
return res.json()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setHeader(key, value) {
|
|
26
|
+
this._headers[key] = value
|
|
27
|
+
return this
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getHeader(key) {
|
|
31
|
+
return this._headers[key]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setBasicAuth(username, password) {
|
|
35
|
+
this._headers.Authorization = `Basic ${btoa(`${username}:${password}`)}`
|
|
36
|
+
return this
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setBearerAuth(token) {
|
|
40
|
+
this._headers.Authorization = `Bearer ${token}`
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async get(endpoint, options = {}) {
|
|
45
|
+
return this._fetchJSON(endpoint, {
|
|
46
|
+
...options,
|
|
47
|
+
method: 'GET',
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async post(endpoint, body, options = {}) {
|
|
52
|
+
return this._fetchJSON(endpoint, {
|
|
53
|
+
...options,
|
|
54
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
55
|
+
method: 'POST',
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async put(endpoint, body, options = {}) {
|
|
60
|
+
return this._fetchJSON(endpoint, {
|
|
61
|
+
...options,
|
|
62
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
63
|
+
method: 'PUT',
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async patch(endpoint, operations, options = {}) {
|
|
68
|
+
return this._fetchJSON(endpoint, {
|
|
69
|
+
parseResponse: false,
|
|
70
|
+
...options,
|
|
71
|
+
body: JSON.stringify(operations),
|
|
72
|
+
method: 'PATCH',
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async delete(endpoint, options = {}) {
|
|
77
|
+
return this._fetchJSON(endpoint, {
|
|
78
|
+
parseResponse: false,
|
|
79
|
+
...options,
|
|
80
|
+
method: 'DELETE',
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
const { DJClient } = require('./index')
|
|
2
|
+
var dockerNames = require('docker-names')
|
|
3
|
+
|
|
4
|
+
test('should return something', async () => {
|
|
5
|
+
const randomName = dockerNames.getRandomName(true)
|
|
6
|
+
const namespace = `integration.javascript.${randomName}`
|
|
7
|
+
const dj = new DJClient('http://localhost:8000')
|
|
8
|
+
|
|
9
|
+
// Create a namespace
|
|
10
|
+
const namespace_created = await dj.namespaces.create(namespace)
|
|
11
|
+
expect(namespace_created).toEqual({
|
|
12
|
+
message: `Node namespace \`${namespace}\` has been successfully created`,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// List namespaces
|
|
16
|
+
const existing_namespaces = await dj.namespaces.list()
|
|
17
|
+
expect(existing_namespaces).toContainEqual({ namespace: namespace })
|
|
18
|
+
|
|
19
|
+
// Create a source
|
|
20
|
+
await dj.sources.create({
|
|
21
|
+
name: `${namespace}.repair_orders`,
|
|
22
|
+
description: 'Repair orders',
|
|
23
|
+
catalog: 'warehouse',
|
|
24
|
+
schema_: 'roads',
|
|
25
|
+
table: 'repair_orders',
|
|
26
|
+
mode: 'published',
|
|
27
|
+
columns: [
|
|
28
|
+
{ name: 'repair_order_id', type: 'int' },
|
|
29
|
+
{ name: 'municipality_id', type: 'string' },
|
|
30
|
+
{ name: 'hard_hat_id', type: 'int' },
|
|
31
|
+
{ name: 'order_date', type: 'timestamp' },
|
|
32
|
+
{ name: 'required_date', type: 'timestamp' },
|
|
33
|
+
{ name: 'dispatched_date', type: 'timestamp' },
|
|
34
|
+
{ name: 'dispatcher_id', type: 'int' },
|
|
35
|
+
],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Get source
|
|
39
|
+
const source1 = await dj.nodes.get(`${namespace}.repair_orders`)
|
|
40
|
+
expect(source1.name).toBe(`${namespace}.repair_orders`)
|
|
41
|
+
expect(source1.status).toBe('valid')
|
|
42
|
+
expect(source1.mode).toBe('published')
|
|
43
|
+
|
|
44
|
+
// Create a transform
|
|
45
|
+
await dj.transforms.create({
|
|
46
|
+
name: `${namespace}.repair_orders_w_dispatchers`,
|
|
47
|
+
description: 'Repair orders that have a dispatcher',
|
|
48
|
+
mode: 'published',
|
|
49
|
+
query: `SELECT
|
|
50
|
+
repair_order_id,
|
|
51
|
+
municipality_id,
|
|
52
|
+
hard_hat_id,
|
|
53
|
+
dispatcher_id
|
|
54
|
+
FROM ${namespace}.repair_orders
|
|
55
|
+
WHERE dispatcher_id IS NOT NULL`,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Get transform
|
|
59
|
+
const transform1 = await dj.nodes.get(
|
|
60
|
+
`${namespace}.repair_orders_w_dispatchers`
|
|
61
|
+
)
|
|
62
|
+
expect(transform1.name).toBe(`${namespace}.repair_orders_w_dispatchers`)
|
|
63
|
+
expect(transform1.status).toBe('valid')
|
|
64
|
+
expect(transform1.mode).toBe('published')
|
|
65
|
+
|
|
66
|
+
// Create a source and dimension node
|
|
67
|
+
const source2 = await dj.sources.create({
|
|
68
|
+
name: `${namespace}.dispatchers`,
|
|
69
|
+
description:
|
|
70
|
+
'Different third party dispatcher companies that coordinate repairs',
|
|
71
|
+
catalog: 'warehouse',
|
|
72
|
+
schema_: 'roads',
|
|
73
|
+
table: 'dispatchers',
|
|
74
|
+
mode: 'published',
|
|
75
|
+
columns: [
|
|
76
|
+
{ name: 'dispatcher_id', type: 'int' },
|
|
77
|
+
{ name: 'company_name', type: 'string' },
|
|
78
|
+
{ name: 'phone', type: 'string' },
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
expect(source2.name).toBe(`${namespace}.dispatchers`)
|
|
82
|
+
expect(source2.status).toBe('valid')
|
|
83
|
+
expect(source2.mode).toBe('published')
|
|
84
|
+
await dj.dimensions.create({
|
|
85
|
+
name: `${namespace}.all_dispatchers`,
|
|
86
|
+
description: 'All dispatchers',
|
|
87
|
+
primary_key: ['dispatcher_id'],
|
|
88
|
+
mode: 'published',
|
|
89
|
+
query: `SELECT
|
|
90
|
+
dispatcher_id,
|
|
91
|
+
company_name,
|
|
92
|
+
phone
|
|
93
|
+
FROM ${namespace}.dispatchers`,
|
|
94
|
+
})
|
|
95
|
+
const dimension1 = await dj.nodes.get(`${namespace}.all_dispatchers`)
|
|
96
|
+
expect(dimension1.name).toBe(`${namespace}.all_dispatchers`)
|
|
97
|
+
expect(dimension1.status).toBe('valid')
|
|
98
|
+
expect(dimension1.mode).toBe('published')
|
|
99
|
+
|
|
100
|
+
// Create metrics
|
|
101
|
+
await dj.metrics.create({
|
|
102
|
+
name: `${namespace}.num_repair_orders`,
|
|
103
|
+
description: 'Number of repair orders',
|
|
104
|
+
mode: 'published',
|
|
105
|
+
query: `SELECT
|
|
106
|
+
count(repair_order_id)
|
|
107
|
+
FROM ${namespace}.repair_orders`,
|
|
108
|
+
})
|
|
109
|
+
const metric1 = await dj.nodes.get(`${namespace}.num_repair_orders`)
|
|
110
|
+
expect(metric1.name).toBe(`${namespace}.num_repair_orders`)
|
|
111
|
+
expect(metric1.status).toBe('valid')
|
|
112
|
+
expect(metric1.mode).toBe('published')
|
|
113
|
+
|
|
114
|
+
// List metrics
|
|
115
|
+
const list_of_metrics = await dj.metrics.all()
|
|
116
|
+
expect(list_of_metrics).toContain(`${namespace}.num_repair_orders`)
|
|
117
|
+
|
|
118
|
+
// Create a dimension link
|
|
119
|
+
const dimension_linked_message = await dj.dimensions.link(
|
|
120
|
+
`${namespace}.repair_orders`,
|
|
121
|
+
'dispatcher_id',
|
|
122
|
+
`${namespace}.all_dispatchers`,
|
|
123
|
+
'dispatcher_id'
|
|
124
|
+
)
|
|
125
|
+
expect(dimension_linked_message).toEqual({
|
|
126
|
+
message: `Dimension node ${namespace}.all_dispatchers has been successfully linked to column dispatcher_id on node ${namespace}.repair_orders`,
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const common_dimensions = await dj.commonDimensions.list([
|
|
130
|
+
'default.num_repair_orders',
|
|
131
|
+
'default.avg_repair_price',
|
|
132
|
+
'default.total_repair_cost',
|
|
133
|
+
])
|
|
134
|
+
|
|
135
|
+
expect(common_dimensions).toEqual([
|
|
136
|
+
{
|
|
137
|
+
name: 'default.dispatcher.company_name',
|
|
138
|
+
type: 'string',
|
|
139
|
+
path: [
|
|
140
|
+
'default.repair_orders.repair_order_id',
|
|
141
|
+
'default.repair_order.dispatcher_id',
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'default.dispatcher.dispatcher_id',
|
|
146
|
+
type: 'int',
|
|
147
|
+
path: [
|
|
148
|
+
'default.repair_orders.repair_order_id',
|
|
149
|
+
'default.repair_order.dispatcher_id',
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'default.dispatcher.phone',
|
|
154
|
+
type: 'string',
|
|
155
|
+
path: [
|
|
156
|
+
'default.repair_orders.repair_order_id',
|
|
157
|
+
'default.repair_order.dispatcher_id',
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'default.hard_hat.address',
|
|
162
|
+
type: 'string',
|
|
163
|
+
path: [
|
|
164
|
+
'default.repair_orders.repair_order_id',
|
|
165
|
+
'default.repair_order.hard_hat_id',
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: 'default.hard_hat.birth_date',
|
|
170
|
+
type: 'date',
|
|
171
|
+
path: [
|
|
172
|
+
'default.repair_orders.repair_order_id',
|
|
173
|
+
'default.repair_order.hard_hat_id',
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'default.hard_hat.city',
|
|
178
|
+
type: 'string',
|
|
179
|
+
path: [
|
|
180
|
+
'default.repair_orders.repair_order_id',
|
|
181
|
+
'default.repair_order.hard_hat_id',
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'default.hard_hat.contractor_id',
|
|
186
|
+
type: 'int',
|
|
187
|
+
path: [
|
|
188
|
+
'default.repair_orders.repair_order_id',
|
|
189
|
+
'default.repair_order.hard_hat_id',
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'default.hard_hat.country',
|
|
194
|
+
type: 'string',
|
|
195
|
+
path: [
|
|
196
|
+
'default.repair_orders.repair_order_id',
|
|
197
|
+
'default.repair_order.hard_hat_id',
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'default.hard_hat.first_name',
|
|
202
|
+
type: 'string',
|
|
203
|
+
path: [
|
|
204
|
+
'default.repair_orders.repair_order_id',
|
|
205
|
+
'default.repair_order.hard_hat_id',
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'default.hard_hat.hard_hat_id',
|
|
210
|
+
type: 'int',
|
|
211
|
+
path: [
|
|
212
|
+
'default.repair_orders.repair_order_id',
|
|
213
|
+
'default.repair_order.hard_hat_id',
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: 'default.hard_hat.hire_date',
|
|
218
|
+
type: 'date',
|
|
219
|
+
path: [
|
|
220
|
+
'default.repair_orders.repair_order_id',
|
|
221
|
+
'default.repair_order.hard_hat_id',
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'default.hard_hat.last_name',
|
|
226
|
+
type: 'string',
|
|
227
|
+
path: [
|
|
228
|
+
'default.repair_orders.repair_order_id',
|
|
229
|
+
'default.repair_order.hard_hat_id',
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'default.hard_hat.manager',
|
|
234
|
+
type: 'int',
|
|
235
|
+
path: [
|
|
236
|
+
'default.repair_orders.repair_order_id',
|
|
237
|
+
'default.repair_order.hard_hat_id',
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'default.hard_hat.postal_code',
|
|
242
|
+
type: 'string',
|
|
243
|
+
path: [
|
|
244
|
+
'default.repair_orders.repair_order_id',
|
|
245
|
+
'default.repair_order.hard_hat_id',
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'default.hard_hat.state',
|
|
250
|
+
type: 'string',
|
|
251
|
+
path: [
|
|
252
|
+
'default.repair_orders.repair_order_id',
|
|
253
|
+
'default.repair_order.hard_hat_id',
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'default.hard_hat.title',
|
|
258
|
+
type: 'string',
|
|
259
|
+
path: [
|
|
260
|
+
'default.repair_orders.repair_order_id',
|
|
261
|
+
'default.repair_order.hard_hat_id',
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'default.municipality_dim.contact_name',
|
|
266
|
+
type: 'string',
|
|
267
|
+
path: [
|
|
268
|
+
'default.repair_orders.repair_order_id',
|
|
269
|
+
'default.repair_order.municipality_id',
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: 'default.municipality_dim.contact_title',
|
|
274
|
+
type: 'string',
|
|
275
|
+
path: [
|
|
276
|
+
'default.repair_orders.repair_order_id',
|
|
277
|
+
'default.repair_order.municipality_id',
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: 'default.municipality_dim.local_region',
|
|
282
|
+
type: 'string',
|
|
283
|
+
path: [
|
|
284
|
+
'default.repair_orders.repair_order_id',
|
|
285
|
+
'default.repair_order.municipality_id',
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'default.municipality_dim.municipality_id',
|
|
290
|
+
type: 'string',
|
|
291
|
+
path: [
|
|
292
|
+
'default.repair_orders.repair_order_id',
|
|
293
|
+
'default.repair_order.municipality_id',
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: 'default.municipality_dim.municipality_type_desc',
|
|
298
|
+
type: 'string',
|
|
299
|
+
path: [
|
|
300
|
+
'default.repair_orders.repair_order_id',
|
|
301
|
+
'default.repair_order.municipality_id',
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'default.municipality_dim.municipality_type_id',
|
|
306
|
+
type: 'string',
|
|
307
|
+
path: [
|
|
308
|
+
'default.repair_orders.repair_order_id',
|
|
309
|
+
'default.repair_order.municipality_id',
|
|
310
|
+
],
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'default.municipality_dim.state_id',
|
|
314
|
+
type: 'int',
|
|
315
|
+
path: [
|
|
316
|
+
'default.repair_orders.repair_order_id',
|
|
317
|
+
'default.repair_order.municipality_id',
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: 'default.repair_orders.repair_order_id',
|
|
322
|
+
type: 'int',
|
|
323
|
+
path: [],
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'default.us_state.state_abbr',
|
|
327
|
+
type: 'string',
|
|
328
|
+
path: [
|
|
329
|
+
'default.repair_orders.repair_order_id',
|
|
330
|
+
'default.repair_order.hard_hat_id',
|
|
331
|
+
'default.hard_hat.state',
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: 'default.us_state.state_id',
|
|
336
|
+
type: 'int',
|
|
337
|
+
path: [
|
|
338
|
+
'default.repair_orders.repair_order_id',
|
|
339
|
+
'default.repair_order.hard_hat_id',
|
|
340
|
+
'default.hard_hat.state',
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: 'default.us_state.state_name',
|
|
345
|
+
type: 'string',
|
|
346
|
+
path: [
|
|
347
|
+
'default.repair_orders.repair_order_id',
|
|
348
|
+
'default.repair_order.hard_hat_id',
|
|
349
|
+
'default.hard_hat.state',
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: 'default.us_state.state_region',
|
|
354
|
+
type: 'int',
|
|
355
|
+
path: [
|
|
356
|
+
'default.repair_orders.repair_order_id',
|
|
357
|
+
'default.repair_order.hard_hat_id',
|
|
358
|
+
'default.hard_hat.state',
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: 'default.us_state.state_region_description',
|
|
363
|
+
type: 'string',
|
|
364
|
+
path: [
|
|
365
|
+
'default.repair_orders.repair_order_id',
|
|
366
|
+
'default.repair_order.hard_hat_id',
|
|
367
|
+
'default.hard_hat.state',
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
])
|
|
371
|
+
|
|
372
|
+
const query = await dj.sql.get(
|
|
373
|
+
[
|
|
374
|
+
'default.num_repair_orders',
|
|
375
|
+
'default.avg_repair_price',
|
|
376
|
+
'default.total_repair_cost',
|
|
377
|
+
],
|
|
378
|
+
[
|
|
379
|
+
'default.us_state.state_abbr',
|
|
380
|
+
'default.us_state.state_id',
|
|
381
|
+
'default.us_state.state_name',
|
|
382
|
+
],
|
|
383
|
+
[]
|
|
384
|
+
)
|
|
385
|
+
const trimmedQuery = query.sql.replace(/\s+/g, '')
|
|
386
|
+
expect(trimmedQuery).toBe(
|
|
387
|
+
`WITH
|
|
388
|
+
m0_default_DOT_num_repair_orders AS (SELECT default_DOT_us_state.state_abbr,
|
|
389
|
+
default_DOT_us_state.state_id,
|
|
390
|
+
default_DOT_us_state.state_name,
|
|
391
|
+
count(default_DOT_repair_orders.repair_order_id) default_DOT_num_repair_orders
|
|
392
|
+
FROM roads.repair_orders AS default_DOT_repair_orders LEFT OUTER JOIN (SELECT default_DOT_repair_orders.dispatcher_id,
|
|
393
|
+
default_DOT_repair_orders.hard_hat_id,
|
|
394
|
+
default_DOT_repair_orders.municipality_id,
|
|
395
|
+
default_DOT_repair_orders.repair_order_id
|
|
396
|
+
FROM roads.repair_orders AS default_DOT_repair_orders)
|
|
397
|
+
AS default_DOT_repair_order ON default_DOT_repair_orders.repair_order_id = default_DOT_repair_order.repair_order_id
|
|
398
|
+
LEFT OUTER JOIN (SELECT default_DOT_hard_hats.birth_date,
|
|
399
|
+
default_DOT_hard_hats.hard_hat_id,
|
|
400
|
+
default_DOT_hard_hats.hire_date,
|
|
401
|
+
default_DOT_hard_hats.state
|
|
402
|
+
FROM roads.hard_hats AS default_DOT_hard_hats)
|
|
403
|
+
AS default_DOT_hard_hat ON default_DOT_repair_order.hard_hat_id = default_DOT_hard_hat.hard_hat_id
|
|
404
|
+
LEFT OUTER JOIN (SELECT default_DOT_us_states.state_abbr,
|
|
405
|
+
default_DOT_us_states.state_id,
|
|
406
|
+
default_DOT_us_states.state_name
|
|
407
|
+
FROM roads.us_states AS default_DOT_us_states LEFT JOIN roads.us_region AS default_DOT_us_region ON default_DOT_us_states.state_region = default_DOT_us_region.us_region_description)
|
|
408
|
+
AS default_DOT_us_state ON default_DOT_hard_hat.state = default_DOT_us_state.state_abbr
|
|
409
|
+
GROUP BY default_DOT_us_state.state_abbr, default_DOT_us_state.state_id, default_DOT_us_state.state_name
|
|
410
|
+
),
|
|
411
|
+
m1_default_DOT_avg_repair_price AS (SELECT default_DOT_us_state.state_abbr,
|
|
412
|
+
default_DOT_us_state.state_id,
|
|
413
|
+
default_DOT_us_state.state_name,
|
|
414
|
+
avg(default_DOT_repair_order_details.price) default_DOT_avg_repair_price
|
|
415
|
+
FROM roads.repair_order_details AS default_DOT_repair_order_details LEFT OUTER JOIN (SELECT default_DOT_repair_orders.dispatcher_id,
|
|
416
|
+
default_DOT_repair_orders.hard_hat_id,
|
|
417
|
+
default_DOT_repair_orders.municipality_id,
|
|
418
|
+
default_DOT_repair_orders.repair_order_id
|
|
419
|
+
FROM roads.repair_orders AS default_DOT_repair_orders)
|
|
420
|
+
AS default_DOT_repair_order ON default_DOT_repair_order_details.repair_order_id = default_DOT_repair_order.repair_order_id
|
|
421
|
+
LEFT OUTER JOIN (SELECT default_DOT_hard_hats.birth_date,
|
|
422
|
+
default_DOT_hard_hats.hard_hat_id,
|
|
423
|
+
default_DOT_hard_hats.hire_date,
|
|
424
|
+
default_DOT_hard_hats.state
|
|
425
|
+
FROM roads.hard_hats AS default_DOT_hard_hats)
|
|
426
|
+
AS default_DOT_hard_hat ON default_DOT_repair_order.hard_hat_id = default_DOT_hard_hat.hard_hat_id
|
|
427
|
+
LEFT OUTER JOIN (SELECT default_DOT_us_states.state_abbr,
|
|
428
|
+
default_DOT_us_states.state_id,
|
|
429
|
+
default_DOT_us_states.state_name
|
|
430
|
+
FROM roads.us_states AS default_DOT_us_states LEFT JOIN roads.us_region AS default_DOT_us_region ON default_DOT_us_states.state_region = default_DOT_us_region.us_region_description)
|
|
431
|
+
AS default_DOT_us_state ON default_DOT_hard_hat.state = default_DOT_us_state.state_abbr
|
|
432
|
+
GROUP BY default_DOT_us_state.state_abbr, default_DOT_us_state.state_id, default_DOT_us_state.state_name
|
|
433
|
+
),
|
|
434
|
+
m2_default_DOT_total_repair_cost AS (SELECT default_DOT_us_state.state_abbr,
|
|
435
|
+
default_DOT_us_state.state_id,
|
|
436
|
+
default_DOT_us_state.state_name,
|
|
437
|
+
sum(default_DOT_repair_order_details.price) default_DOT_total_repair_cost
|
|
438
|
+
FROM roads.repair_order_details AS default_DOT_repair_order_details LEFT OUTER JOIN (SELECT default_DOT_repair_orders.dispatcher_id,
|
|
439
|
+
default_DOT_repair_orders.hard_hat_id,
|
|
440
|
+
default_DOT_repair_orders.municipality_id,
|
|
441
|
+
default_DOT_repair_orders.repair_order_id
|
|
442
|
+
FROM roads.repair_orders AS default_DOT_repair_orders)
|
|
443
|
+
AS default_DOT_repair_order ON default_DOT_repair_order_details.repair_order_id = default_DOT_repair_order.repair_order_id
|
|
444
|
+
LEFT OUTER JOIN (SELECT default_DOT_hard_hats.birth_date,
|
|
445
|
+
default_DOT_hard_hats.hard_hat_id,
|
|
446
|
+
default_DOT_hard_hats.hire_date,
|
|
447
|
+
default_DOT_hard_hats.state
|
|
448
|
+
FROM roads.hard_hats AS default_DOT_hard_hats)
|
|
449
|
+
AS default_DOT_hard_hat ON default_DOT_repair_order.hard_hat_id = default_DOT_hard_hat.hard_hat_id
|
|
450
|
+
LEFT OUTER JOIN (SELECT default_DOT_us_states.state_abbr,
|
|
451
|
+
default_DOT_us_states.state_id,
|
|
452
|
+
default_DOT_us_states.state_name
|
|
453
|
+
FROM roads.us_states AS default_DOT_us_states LEFT JOIN roads.us_region AS default_DOT_us_region ON default_DOT_us_states.state_region = default_DOT_us_region.us_region_description)
|
|
454
|
+
AS default_DOT_us_state ON default_DOT_hard_hat.state = default_DOT_us_state.state_abbr
|
|
455
|
+
GROUP BY default_DOT_us_state.state_abbr, default_DOT_us_state.state_id, default_DOT_us_state.state_name
|
|
456
|
+
)SELECT m0_default_DOT_num_repair_orders.default_DOT_num_repair_orders,
|
|
457
|
+
m1_default_DOT_avg_repair_price.default_DOT_avg_repair_price,
|
|
458
|
+
m2_default_DOT_total_repair_cost.default_DOT_total_repair_cost,
|
|
459
|
+
COALESCE(m0_default_DOT_num_repair_orders.state_abbr, m1_default_DOT_avg_repair_price.state_abbr, m2_default_DOT_total_repair_cost.state_abbr) state_abbr,
|
|
460
|
+
COALESCE(m0_default_DOT_num_repair_orders.state_id, m1_default_DOT_avg_repair_price.state_id, m2_default_DOT_total_repair_cost.state_id) state_id,
|
|
461
|
+
COALESCE(m0_default_DOT_num_repair_orders.state_name, m1_default_DOT_avg_repair_price.state_name, m2_default_DOT_total_repair_cost.state_name) state_name
|
|
462
|
+
FROM m0_default_DOT_num_repair_orders FULL OUTER JOIN m1_default_DOT_avg_repair_price ON m0_default_DOT_num_repair_orders.state_abbr = m1_default_DOT_avg_repair_price.state_abbr AND m0_default_DOT_num_repair_orders.state_id = m1_default_DOT_avg_repair_price.state_id AND m0_default_DOT_num_repair_orders.state_name = m1_default_DOT_avg_repair_price.state_name
|
|
463
|
+
FULL OUTER JOIN m2_default_DOT_total_repair_cost ON m0_default_DOT_num_repair_orders.state_abbr = m2_default_DOT_total_repair_cost.state_abbr AND m0_default_DOT_num_repair_orders.state_id = m2_default_DOT_total_repair_cost.state_id AND m0_default_DOT_num_repair_orders.state_name = m2_default_DOT_total_repair_cost.state_name
|
|
464
|
+
`.replace(/\s+/g, '')
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
const data = await dj.data.get(
|
|
468
|
+
[
|
|
469
|
+
'default.num_repair_orders',
|
|
470
|
+
'default.avg_repair_price',
|
|
471
|
+
'default.total_repair_cost',
|
|
472
|
+
],
|
|
473
|
+
[
|
|
474
|
+
'default.us_state.state_abbr',
|
|
475
|
+
'default.us_state.state_id',
|
|
476
|
+
'default.us_state.state_name',
|
|
477
|
+
],
|
|
478
|
+
[]
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
expect(data).toEqual({
|
|
482
|
+
columns: [
|
|
483
|
+
{
|
|
484
|
+
name: 'default_DOT_num_repair_orders',
|
|
485
|
+
type: 'bigint',
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: 'default_DOT_avg_repair_price',
|
|
489
|
+
type: 'double',
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: 'default_DOT_total_repair_cost',
|
|
493
|
+
type: 'double',
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: 'state_abbr',
|
|
497
|
+
type: 'string',
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
name: 'state_id',
|
|
501
|
+
type: 'int',
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: 'state_name',
|
|
505
|
+
type: 'string',
|
|
506
|
+
},
|
|
507
|
+
],
|
|
508
|
+
data: [
|
|
509
|
+
[486, 65682.0, 31921452.0, 'AZ', 3, 'Arizona'],
|
|
510
|
+
[486, 39301.5, 19100529.0, 'CT', 7, 'Connecticut'],
|
|
511
|
+
[729, 65595.66666666667, 47819241.0, 'GA', 11, 'Georgia'],
|
|
512
|
+
[729, 76555.33333333333, 55808838.0, 'MA', 22, 'Massachusetts'],
|
|
513
|
+
[1215, 64190.6, 77991579.0, 'MI', 23, 'Michigan'],
|
|
514
|
+
[972, 54672.75, 53141913.0, 'NJ', 31, 'New Jersey'],
|
|
515
|
+
[243, 53374.0, 12969882.0, 'NY', 33, 'New York'],
|
|
516
|
+
[243, 70418.0, 17111574.0, 'OK', 37, 'Oklahoma'],
|
|
517
|
+
[972, 54083.5, 52569162.0, 'PA', 39, 'Pennsylvania'],
|
|
518
|
+
],
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
// Create a transform 2 downstream from a transform 1
|
|
522
|
+
await dj.transforms.create({
|
|
523
|
+
name: `${namespace}.repair_orders_w_hard_hats`,
|
|
524
|
+
description: 'Repair orders that have a hard hat assigned',
|
|
525
|
+
mode: 'published',
|
|
526
|
+
query: `SELECT
|
|
527
|
+
repair_order_id,
|
|
528
|
+
municipality_id,
|
|
529
|
+
hard_hat_id,
|
|
530
|
+
dispatcher_id
|
|
531
|
+
FROM ${namespace}.repair_orders_w_dispatchers
|
|
532
|
+
WHERE hard_hat_id IS NOT NULL`,
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
// Get transform 2 that's downstream from transform 1 and make sure it's valid
|
|
536
|
+
const transform2 = await dj.nodes.get(
|
|
537
|
+
`${namespace}.repair_orders_w_hard_hats`
|
|
538
|
+
)
|
|
539
|
+
expect(transform2.name).toBe(`${namespace}.repair_orders_w_hard_hats`)
|
|
540
|
+
expect(transform2.status).toBe('valid')
|
|
541
|
+
expect(transform2.mode).toBe('published')
|
|
542
|
+
|
|
543
|
+
// Create a draft transform 4 that's downstream from a not yet created transform 3
|
|
544
|
+
await dj.transforms.create({
|
|
545
|
+
name: `${namespace}.repair_orders_w_repair_order_id`,
|
|
546
|
+
description: 'Repair orders without a null ID',
|
|
547
|
+
mode: 'draft',
|
|
548
|
+
query: `SELECT
|
|
549
|
+
repair_order_id,
|
|
550
|
+
municipality_id,
|
|
551
|
+
hard_hat_id,
|
|
552
|
+
dispatcher_id
|
|
553
|
+
FROM ${namespace}.repair_orders_w_municipalities
|
|
554
|
+
WHERE repair_order_id IS NOT NULL`,
|
|
555
|
+
})
|
|
556
|
+
const transform4 = await dj.nodes.get(
|
|
557
|
+
`${namespace}.repair_orders_w_repair_order_id`
|
|
558
|
+
)
|
|
559
|
+
// Check that transform 4 is invalid because transform 3 does not exist
|
|
560
|
+
expect(transform4.name).toBe(`${namespace}.repair_orders_w_repair_order_id`)
|
|
561
|
+
expect(transform4.status).toBe('invalid')
|
|
562
|
+
expect(transform4.mode).toBe('draft')
|
|
563
|
+
|
|
564
|
+
// Create a draft transform 3 that's downstream from transform 2
|
|
565
|
+
await dj.transforms.create({
|
|
566
|
+
name: `${namespace}.repair_orders_w_municipalities`,
|
|
567
|
+
description: 'Repair orders that have a municipality listed',
|
|
568
|
+
mode: 'draft',
|
|
569
|
+
query: `SELECT
|
|
570
|
+
repair_order_id,
|
|
571
|
+
municipality_id,
|
|
572
|
+
hard_hat_id,
|
|
573
|
+
dispatcher_id
|
|
574
|
+
FROM ${namespace}.repair_orders_w_hard_hats
|
|
575
|
+
WHERE municipality_id IS NOT NULL`,
|
|
576
|
+
})
|
|
577
|
+
const transform3 = await dj.nodes.get(
|
|
578
|
+
`${namespace}.repair_orders_w_municipalities`
|
|
579
|
+
)
|
|
580
|
+
expect(transform3.name).toBe(`${namespace}.repair_orders_w_municipalities`)
|
|
581
|
+
expect(transform3.status).toBe('valid')
|
|
582
|
+
expect(transform3.mode).toBe('draft')
|
|
583
|
+
|
|
584
|
+
// Check that transform 4 is now valid after transform 3 was created
|
|
585
|
+
const transform4_now_valid = await dj.nodes.get(
|
|
586
|
+
`${namespace}.repair_orders_w_repair_order_id`
|
|
587
|
+
)
|
|
588
|
+
expect(transform4_now_valid.status).toBe('valid')
|
|
589
|
+
|
|
590
|
+
// Check that publishing transform 3 works
|
|
591
|
+
await dj.nodes.publish(transform3.name)
|
|
592
|
+
}, 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
|
+
}
|