@malloydata/malloy-query-builder 0.0.237-dev250225144145
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/DEVELOPING.md +27 -0
- package/README.md +437 -0
- package/dist/expects.d.ts +26 -0
- package/dist/expects.js +149 -0
- package/dist/expects.js.map +1 -0
- package/dist/flights_model.d.ts +2 -0
- package/dist/flights_model.js +2146 -0
- package/dist/flights_model.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/query-ast.d.ts +1614 -0
- package/dist/query-ast.js +3633 -0
- package/dist/query-ast.js.map +1 -0
- package/dist/query-ast.spec.d.ts +1 -0
- package/dist/query-ast.spec.js +1776 -0
- package/dist/query-ast.spec.js.map +1 -0
- package/flow/flights_model.d.ts +2 -0
- package/flow/index.d.ts +1 -0
- package/flow/query-ast.d.ts +1100 -0
- package/package.json +34 -0
- package/scripts/flow-api-translator.d.ts +3 -0
- package/scripts/gen_flow.ts +23 -0
- package/src/expects.ts +194 -0
- package/src/flights_model.ts +2150 -0
- package/src/index.ts +8 -0
- package/src/query-ast.spec.ts +1777 -0
- package/src/query-ast.ts +4654 -0
- package/tsconfig.json +11 -0
package/DEVELOPING.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Things Flow converter does not support
|
|
2
|
+
|
|
3
|
+
Constructor types:
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
type NodeConstructor<T extends Node> = new (...args: any[]) => T;
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Computed signature names, including ones for basic symbols like `Symbol.iterator`:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
*[Symbol.iterator](): Generator<N, void, unknown> {
|
|
13
|
+
for (let i = 0; i < this.length; i++) {
|
|
14
|
+
yield this.index(i);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Instead, name it something concrete, like `iter()`. Which is annoying because then you can't iterate on it directly.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
*iter(): Generator<N, void, unknown> {
|
|
23
|
+
for (let i = 0; i < this.length; i++) {
|
|
24
|
+
yield this.index(i);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
|
|
2
|
+
# Malloy Query AST
|
|
3
|
+
|
|
4
|
+
Get started with the {@link ASTQuery} class.
|
|
5
|
+
|
|
6
|
+
## Serialize itself to a Malloy Query string
|
|
7
|
+
|
|
8
|
+
{@link ASTQuery.toMalloy}
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
query.setSource('flights');
|
|
12
|
+
query.setView('by_carrier');
|
|
13
|
+
query.toMalloy();
|
|
14
|
+
```
|
|
15
|
+
```
|
|
16
|
+
run: flights -> by_carrier
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Provide an interface to walk the tree
|
|
20
|
+
|
|
21
|
+
This is for the Explorer, e.g., to create the query summary UI
|
|
22
|
+
|
|
23
|
+
## To the empty query, add a starting point which is either a new literal view or a view reference which can later be refined
|
|
24
|
+
|
|
25
|
+
{@link ASTQuery.getOrAddDefaultSegment}
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
query.setSource('flights');
|
|
29
|
+
query.getOrAddDefaultSegment().addGroupBy("carrier");
|
|
30
|
+
```
|
|
31
|
+
```
|
|
32
|
+
run: flights -> { group_by: carrier }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
{@link ASTQuery.setViewToEmptySegment}
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
query.setSource('flights');
|
|
39
|
+
query.setViewToEmptySegment().addGroupBy("carrier");
|
|
40
|
+
```
|
|
41
|
+
```
|
|
42
|
+
run: flights -> { group_by: carrier }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
{@link ASTQuery.setView}
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
query.setSource('flights');
|
|
49
|
+
query.setView('by_carrier');
|
|
50
|
+
```
|
|
51
|
+
```
|
|
52
|
+
run: flights -> by_carrier
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Determine if the query can be run
|
|
56
|
+
|
|
57
|
+
{@link ASTSegmentViewDefinition.isRunnable}
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
run: flights -> { }
|
|
61
|
+
```
|
|
62
|
+
```ts
|
|
63
|
+
query.isRunnable() // false
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Add a new field to a particular literal view
|
|
67
|
+
|
|
68
|
+
{@link ASTSegmentViewDefinition.addGroupBy}
|
|
69
|
+
{@link ASTSegmentViewDefinition.addAggregate}
|
|
70
|
+
{@link ASTSegmentViewDefinition.addNest}
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const segment = query.getOrAddDefaultSegment();
|
|
74
|
+
segment.addGroupBy('carrier');
|
|
75
|
+
segment.addAggregate('flight_count');
|
|
76
|
+
segment.addNest('by_origin');
|
|
77
|
+
```
|
|
78
|
+
```
|
|
79
|
+
run: flights -> {
|
|
80
|
+
group_by: carrier
|
|
81
|
+
aggregate: flight_count
|
|
82
|
+
nest: by_origin
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## A field reference
|
|
87
|
+
## A time truncation of a field reference
|
|
88
|
+
|
|
89
|
+
{@link ASTSegmentViewDefinition.addDateGroupBy}
|
|
90
|
+
{@link ASTSegmentViewDefinition.addTimestampGroupBy}
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const segment = query.getOrAddDefaultSegment();
|
|
94
|
+
segment.addTimestampGroupBy('dep_time', 'month');
|
|
95
|
+
```
|
|
96
|
+
```
|
|
97
|
+
run: flights -> {
|
|
98
|
+
group_by: dep_time.month
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## A measure reference with filters
|
|
103
|
+
|
|
104
|
+
## Rename/delete a field
|
|
105
|
+
|
|
106
|
+
{@link ASTGroupByViewOperation.delete}
|
|
107
|
+
{@link ASTAggregateViewOperation.delete}
|
|
108
|
+
{@link ASTNestViewOperation.delete}
|
|
109
|
+
{@link ASTGroupByViewOperation.rename}
|
|
110
|
+
{@link ASTAggregateViewOperation.rename}
|
|
111
|
+
{@link ASTNestViewOperation.rename}
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
run: flights -> {
|
|
115
|
+
group_by: carrier
|
|
116
|
+
aggregate: flight_count
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
```ts
|
|
120
|
+
groupBy.delete();
|
|
121
|
+
aggregate.rename("flight_count_2");
|
|
122
|
+
```
|
|
123
|
+
```
|
|
124
|
+
run: flights -> { aggregate: flight_count_2 is flight_count }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Check if a field is present
|
|
128
|
+
|
|
129
|
+
{@link ASTSegmentViewDefinition.hasField}
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
query.getOrAddDefaultSegment().hasField('carrier');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Add/edit/delete order by
|
|
136
|
+
|
|
137
|
+
{@link ASTOrderByViewOperation.delete}
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
run: flights -> {
|
|
141
|
+
group_by: carrier
|
|
142
|
+
order_by: carrier desc
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
```ts
|
|
146
|
+
orderBy.delete();
|
|
147
|
+
```
|
|
148
|
+
```
|
|
149
|
+
run: flights -> { group_by: carrier }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
{@link ASTOrderByViewOperation.setField}
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
run: flights -> {
|
|
156
|
+
group_by:
|
|
157
|
+
carrier
|
|
158
|
+
flight_count
|
|
159
|
+
order_by: carrier desc
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
```ts
|
|
163
|
+
orderBy.setField("flight_count");
|
|
164
|
+
```
|
|
165
|
+
```
|
|
166
|
+
run: flights -> {
|
|
167
|
+
group_by:
|
|
168
|
+
carrier
|
|
169
|
+
flight_count
|
|
170
|
+
order_by: flight_count desc
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
{@link ASTOrderByViewOperation.setDirection}
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
run: flights -> {
|
|
178
|
+
group_by: carrier
|
|
179
|
+
order_by: carrier desc
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
```ts
|
|
183
|
+
orderBy.setDirection(Malloy.OrderByDirection.ASC);
|
|
184
|
+
```
|
|
185
|
+
```
|
|
186
|
+
run: flights -> {
|
|
187
|
+
group_by: carrier
|
|
188
|
+
order_by: flight_count asc
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Add/edit/delete filter
|
|
193
|
+
|
|
194
|
+
* {@link ASTSegmentViewDefinition.addWhere}
|
|
195
|
+
* {@link ASTFilterWithFilterString.setFilterString}
|
|
196
|
+
* {@link ASTFilterWithFilterString.setFilter}
|
|
197
|
+
* {@link ASTFilterWithFilterString.getFilter}
|
|
198
|
+
* {@link ASTWhereViewOperation.delete}
|
|
199
|
+
* {@link ASTWhere.delete}
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
query.getOrAddDefaultSegment().addWhere("carrier", "WN, AA");
|
|
203
|
+
```
|
|
204
|
+
```
|
|
205
|
+
run: flights -> { where: carrier ~ f`WN, AA` }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Add/edit/delete limit
|
|
209
|
+
|
|
210
|
+
* {@link ASTSegmentViewDefinition.setLimit}
|
|
211
|
+
* {@link ASTLimitViewOperation.delete}
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
query.getOrAddDefaultSegment().setLimit(10);
|
|
215
|
+
```
|
|
216
|
+
```
|
|
217
|
+
run: flights -> { limit: 10 }
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Create new nest with name
|
|
221
|
+
|
|
222
|
+
{@link ASTSegmentViewDefinition.addEmptyNest}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
query.getOrAddDefaultSegment().addEmptyNest("by_origin");
|
|
227
|
+
```
|
|
228
|
+
```
|
|
229
|
+
run: flights -> { nest: by_origin is { } }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Reorder fields
|
|
233
|
+
## To a particular nesting level (literal view or view + refinements), reorder fields
|
|
234
|
+
|
|
235
|
+
## For a particular literal view, list the fields which can be added
|
|
236
|
+
|
|
237
|
+
{@link ASTSegmentViewDefinition.getInputSchema}
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
query.getOrAddDefaultSegment().getInputSchema();
|
|
241
|
+
```
|
|
242
|
+
```
|
|
243
|
+
{
|
|
244
|
+
fields: [
|
|
245
|
+
{ kind: "measure", name: "flight_count", type: { kind: "string_type" }}
|
|
246
|
+
...
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## To a particular view reference, add a new literal view as a refinement
|
|
252
|
+
|
|
253
|
+
{@link IASTViewDefinition.addEmptyRefinement}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const view = query.setView("by_carrier");
|
|
258
|
+
const segment = view.addEmptyRefinement();
|
|
259
|
+
segment.setLimit(10);
|
|
260
|
+
```
|
|
261
|
+
```
|
|
262
|
+
run: flights -> by_carrier + { limit: 10 }
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## To a particular view reference, add a new view reference as a refinement
|
|
266
|
+
|
|
267
|
+
{@link IASTViewDefinition.addViewRefinement}
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
const view = query.setView("by_carrier");
|
|
271
|
+
view.addViewRefinement("top10");
|
|
272
|
+
```
|
|
273
|
+
```
|
|
274
|
+
run: flights -> by_carrier + top10
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## To a particular view (literal or reference), specify the order of fields by way of adding/editing an annotation
|
|
278
|
+
|
|
279
|
+
{@link ASTQuery.reorderFields}
|
|
280
|
+
{@link ASTView.reorderFields}
|
|
281
|
+
|
|
282
|
+
If the view or query is a simple segment, it will automatically reorder the clauses.
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
run: flights -> {
|
|
286
|
+
group_by: carrier
|
|
287
|
+
aggregate: flight_count
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
```ts
|
|
291
|
+
query.reorderFields(['flight_count', 'carrier']);
|
|
292
|
+
```
|
|
293
|
+
```
|
|
294
|
+
run: flights -> {
|
|
295
|
+
aggregate: flight_count
|
|
296
|
+
group_by: carrier
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Otherwise, it will add an annotation:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
run: flights -> by_carrier
|
|
304
|
+
```
|
|
305
|
+
```ts
|
|
306
|
+
query.reorderFields(['flight_count', 'carrier']);
|
|
307
|
+
```
|
|
308
|
+
```
|
|
309
|
+
# field_order = [flight_count, carrier]
|
|
310
|
+
run: flights -> by_carrier
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## To a particular aggregate field in the query, add/edit/delete filter
|
|
314
|
+
|
|
315
|
+
* {@link ASTAggregateViewOperation.addWhere}
|
|
316
|
+
* {@link ASTFilterWithFilterString.setFilter}
|
|
317
|
+
* {@link ASTWhere.delete}
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
query.getOrAddDefaultSegment().addAggregate('flight_count').addWhere('carrier', 'WN, AA');
|
|
321
|
+
```
|
|
322
|
+
```
|
|
323
|
+
run: flights -> { aggregate: flight_count { where: carrier ~ f`WN, AA`} }
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Specify or remove source parameter value
|
|
327
|
+
|
|
328
|
+
{@link ASTSourceReference.setParameter}
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
query.source.setParameter("param", 1);
|
|
332
|
+
```
|
|
333
|
+
```
|
|
334
|
+
run: flights(param is 1) ->
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## List parameters of the source and whether they are required
|
|
338
|
+
|
|
339
|
+
{@link ASTSourceReference.getSourceParameters}
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
query.definition.asArrowQueryDefinition().source.getSourceParameters();
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## To a particular field in the query (including nests), add/edit/delete annotation
|
|
346
|
+
|
|
347
|
+
{@link IASTAnnotatable.setTagProperty}
|
|
348
|
+
{@link IASTAnnotatable.removeTagProperty}
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
query
|
|
352
|
+
.getOrAddDefaultSegment()
|
|
353
|
+
.addGroupBy('carrier');
|
|
354
|
+
.setTagProperty(['a', 'b', 'c'], 10);
|
|
355
|
+
```
|
|
356
|
+
```
|
|
357
|
+
run: flights -> {
|
|
358
|
+
# a.b.c = 10
|
|
359
|
+
group_by: carrier
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
// Assume that 'by_carrier' has, in the model, a tag "bar_chart"
|
|
365
|
+
query
|
|
366
|
+
.getOrAddDefaultSegment()
|
|
367
|
+
.addNest('by_carrier');
|
|
368
|
+
.removeTagProperty(['bar_chart']);
|
|
369
|
+
```
|
|
370
|
+
```
|
|
371
|
+
run: flights -> {
|
|
372
|
+
# -bar_chart
|
|
373
|
+
nest: by_carrier
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## To a particular field, ask which annotations come from the input field vs in the query itself
|
|
378
|
+
|
|
379
|
+
{@link IASTAnnotatable.getIntrinsicTag}
|
|
380
|
+
{@link IASTAnnotatable.getInheritedTag}
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
query
|
|
384
|
+
.getOrAddDefaultSegment()
|
|
385
|
+
.addGroupBy('carrier');
|
|
386
|
+
.getIntrinsicTag()
|
|
387
|
+
.has('some_tag');
|
|
388
|
+
query
|
|
389
|
+
.getOrAddDefaultSegment()
|
|
390
|
+
.addGroupBy('carrier');
|
|
391
|
+
.getInheritedTag()
|
|
392
|
+
.has('some_tag');
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## To the query itself, add/edit/delete annotation
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
query.setTagProperty(['bar_chart']);
|
|
399
|
+
query.setSource('flights');
|
|
400
|
+
query.setView('by_carrier');
|
|
401
|
+
```
|
|
402
|
+
```
|
|
403
|
+
# bar_chart
|
|
404
|
+
run: flights -> by_carrier
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## After any operation of a QueryBuilder, perform a partial validation of the query
|
|
408
|
+
This is only ever a partial validation: cube resolution, aggregate validation, and expression validation (and possibly other validation) must happen in the translator, and will not be replicated in the QueryBuilder
|
|
409
|
+
We will do as much validation as we can do, but it is possible some queries will only generate errors when you do a full translation (probably when you run it)
|
|
410
|
+
## Given a filter string and a field type, parse it into a StableFilterDef
|
|
411
|
+
## Given a StableFilterDef, serialize it into a filter string
|
|
412
|
+
## Automatically determine where in a literal view is most appropriate to place a new field
|
|
413
|
+
|
|
414
|
+
This happens automatically when you call {@link ASTSegmentViewDefinition.addGroupBy}, {@link ASTSegmentViewDefinition.addAggregate}, {@link ASTSegmentViewDefinition.addNest}, {@link ASTSegmentViewDefinition.addWhere}, {@link ASTSegmentViewDefinition.addOrderBy}, {@link ASTSegmentViewDefinition.setLimit}, etc..
|
|
415
|
+
|
|
416
|
+
## Finding the default place in the tree to put a new field
|
|
417
|
+
|
|
418
|
+
{@link ASTQuery.getOrAddDefaultSegment}
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
query.setSource('flights');
|
|
422
|
+
query.getOrAddDefaultSegment().addGroupBy("carrier");
|
|
423
|
+
```
|
|
424
|
+
```
|
|
425
|
+
run: flights -> { group_by: carrier }
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
run: flights -> by_carrier
|
|
430
|
+
```
|
|
431
|
+
```ts
|
|
432
|
+
query.setSource('flights');
|
|
433
|
+
query.getOrAddDefaultSegment().setLimit(10);
|
|
434
|
+
```
|
|
435
|
+
```
|
|
436
|
+
run: flights -> by_carrier + { limit: 10 }
|
|
437
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as Malloy from '@malloydata/malloy-interfaces';
|
|
2
|
+
declare global {
|
|
3
|
+
namespace jest {
|
|
4
|
+
interface Matchers<R> {
|
|
5
|
+
/**
|
|
6
|
+
* expect(q => q.getOrCreateDefaultSegment().addGroupBy('carrier')).toModifyQuery({
|
|
7
|
+
* from: ...,
|
|
8
|
+
* to: ...,
|
|
9
|
+
* malloy: 'run: flights -> { group_by: carrier }'
|
|
10
|
+
* });
|
|
11
|
+
*/
|
|
12
|
+
toModifyQuery(exp: {
|
|
13
|
+
model: Malloy.ModelInfo;
|
|
14
|
+
from: Malloy.Query;
|
|
15
|
+
to: Malloy.Query;
|
|
16
|
+
malloy: string;
|
|
17
|
+
}): R;
|
|
18
|
+
toModifyQuery(exp: {
|
|
19
|
+
source: Malloy.SourceInfo;
|
|
20
|
+
from: Malloy.Query;
|
|
21
|
+
to: Malloy.Query;
|
|
22
|
+
malloy: string;
|
|
23
|
+
}): R;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/dist/expects.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const query_ast_1 = require("./query-ast");
|
|
10
|
+
expect.extend({
|
|
11
|
+
toModifyQuery(f, { model, source, from, to, malloy, }) {
|
|
12
|
+
const clone = JSON.parse(JSON.stringify(from));
|
|
13
|
+
const q = model
|
|
14
|
+
? new query_ast_1.ASTQuery({ model, query: from })
|
|
15
|
+
: source
|
|
16
|
+
? new query_ast_1.ASTQuery({ source, query: from })
|
|
17
|
+
: undefined;
|
|
18
|
+
if (q === undefined) {
|
|
19
|
+
throw new Error('Must specify either model or source');
|
|
20
|
+
}
|
|
21
|
+
f(q);
|
|
22
|
+
const query = q.build();
|
|
23
|
+
const eq = objectsMatch(query, to);
|
|
24
|
+
const diff = this.utils.diff(to, query);
|
|
25
|
+
if (!eq) {
|
|
26
|
+
return {
|
|
27
|
+
pass: false,
|
|
28
|
+
message: () => `Modified query object does not match expected: ${diff}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
ensureOnlyMinimalEdits(from, query, clone);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return {
|
|
36
|
+
pass: false,
|
|
37
|
+
message: () => `Resulting query object should have minimal edits: ${error.message}`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const actualMalloy = q.toMalloy();
|
|
41
|
+
const malloyDiff = this.utils.diff(malloy, actualMalloy);
|
|
42
|
+
if (malloy !== actualMalloy) {
|
|
43
|
+
return {
|
|
44
|
+
pass: false,
|
|
45
|
+
message: () => `Resulting query text does not match expected: ${malloyDiff}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
pass: true,
|
|
50
|
+
message: () => 'Result matched',
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
function ensureOnlyMinimalEdits(
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
a,
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
b,
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
aClone, path = []) {
|
|
61
|
+
if (typeof a === 'string' ||
|
|
62
|
+
typeof a === 'number' ||
|
|
63
|
+
typeof a === 'boolean') {
|
|
64
|
+
return aClone !== b;
|
|
65
|
+
}
|
|
66
|
+
let different = false;
|
|
67
|
+
if (Array.isArray(a)) {
|
|
68
|
+
different = aClone.length !== b.length;
|
|
69
|
+
for (let i = 0; i < aClone.length || i < b.length; i++) {
|
|
70
|
+
if (a === undefined || b === undefined) {
|
|
71
|
+
different = true;
|
|
72
|
+
}
|
|
73
|
+
else if (aClone[i] === b[i]) {
|
|
74
|
+
different || (different = ensureOnlyMinimalEdits(a[i], b[i], aClone[i], [
|
|
75
|
+
...path,
|
|
76
|
+
i,
|
|
77
|
+
]));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
different = true;
|
|
81
|
+
const found = aClone.findIndex(f => f === b[i]);
|
|
82
|
+
if (found !== -1) {
|
|
83
|
+
ensureOnlyMinimalEdits(a[found], b[i], aClone[found], [...path, i]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
for (const key in aClone) {
|
|
90
|
+
different || (different = ensureOnlyMinimalEdits(a[key], b[key], aClone[key], [
|
|
91
|
+
...path,
|
|
92
|
+
key,
|
|
93
|
+
]));
|
|
94
|
+
}
|
|
95
|
+
for (const key in b) {
|
|
96
|
+
if (key in aClone)
|
|
97
|
+
continue;
|
|
98
|
+
different = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const sameObject = a === b;
|
|
102
|
+
if (different) {
|
|
103
|
+
if (sameObject) {
|
|
104
|
+
throw new Error(`Path /${path.join('/')} was illegally mutated`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
if (!sameObject) {
|
|
109
|
+
throw new Error(`Path /${path.join('/')} was unnecessarily cloned`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return different;
|
|
113
|
+
}
|
|
114
|
+
function objectsMatch(a, b) {
|
|
115
|
+
if (typeof b === 'string' ||
|
|
116
|
+
typeof b === 'number' ||
|
|
117
|
+
typeof b === 'boolean' ||
|
|
118
|
+
typeof b === 'bigint' ||
|
|
119
|
+
b === undefined ||
|
|
120
|
+
b === null) {
|
|
121
|
+
return b === a;
|
|
122
|
+
}
|
|
123
|
+
else if (Array.isArray(b)) {
|
|
124
|
+
if (Array.isArray(a)) {
|
|
125
|
+
return a.length === b.length && a.every((v, i) => objectsMatch(v, b[i]));
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (typeof a === 'string' ||
|
|
131
|
+
typeof a === 'number' ||
|
|
132
|
+
typeof a === 'boolean' ||
|
|
133
|
+
typeof a === 'bigint' ||
|
|
134
|
+
a === undefined ||
|
|
135
|
+
a === null) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
if (Array.isArray(a))
|
|
139
|
+
return false;
|
|
140
|
+
const keys = Object.keys(b);
|
|
141
|
+
for (const key of keys) {
|
|
142
|
+
if (!objectsMatch(a[key], b[key])) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=expects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expects.js","sourceRoot":"","sources":["../src/expects.ts"],"names":[],"mappings":"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"}
|