Skip to content

Commit 42289a9

Browse files
authored
Merge pull request #673 from jvalue/rfc17-implementation
[RFC0017] Add `invalid` and `missing`error values
2 parents 368e22c + 2650617 commit 42289a9

File tree

62 files changed

+1135
-707
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1135
-707
lines changed

libs/execution/src/lib/blocks/composite-block-executor.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import {
1010
type BlockTypePipeline,
1111
type BlockTypeProperty,
1212
type CompositeBlockTypeDefinition,
13+
ERROR_TYPEGUARD,
1314
type EvaluationContext,
1415
IOType,
15-
type InternalValueRepresentation,
16+
type InternalErrorValueRepresentation,
17+
type InternalValidValueRepresentation,
18+
MissingValue,
1619
type ValueType,
1720
type WrapperFactoryProvider,
1821
evaluateExpression,
@@ -165,11 +168,6 @@ export function createCompositeBlockExecutor(
165168
context.wrapperFactories,
166169
);
167170

168-
assert(
169-
propertyValue !== undefined,
170-
`Can not get value for block type property ${blockTypeProperty.name}`,
171-
);
172-
173171
context.evaluationContext.setValueForReference(
174172
blockTypeProperty.name,
175173
propertyValue,
@@ -184,7 +182,7 @@ export function createCompositeBlockExecutor(
184182
properties: BlockTypeProperty[],
185183
evaluationContext: EvaluationContext,
186184
wrapperFactories: WrapperFactoryProvider,
187-
): InternalValueRepresentation | undefined {
185+
): InternalValidValueRepresentation | InternalErrorValueRepresentation {
188186
const propertyFromBlock = block.body.properties.find(
189187
(property) => property.name === name,
190188
);
@@ -197,7 +195,7 @@ export function createCompositeBlockExecutor(
197195
valueType,
198196
);
199197

200-
if (value !== undefined) {
198+
if (!ERROR_TYPEGUARD(value)) {
201199
return value;
202200
}
203201
}
@@ -207,7 +205,9 @@ export function createCompositeBlockExecutor(
207205
);
208206

209207
if (propertyFromBlockType?.defaultValue === undefined) {
210-
return;
208+
return new MissingValue(
209+
`Could not find default value for property ${name}`,
210+
);
211211
}
212212

213213
return evaluateExpression(

libs/execution/src/lib/constraints/constraint-executor.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { strict as assert } from 'assert';
88
import {
99
type AstNodeWrapper,
1010
type ConstraintDefinition,
11-
type InternalValueRepresentation,
11+
ERROR_TYPEGUARD,
12+
type InternalErrorValueRepresentation,
13+
type InternalValidValueRepresentation,
1214
type ValueTypeAttribute,
1315
type ValueTypeConstraintInlineDefinition,
1416
evaluateExpression,
@@ -23,7 +25,7 @@ export class ConstraintExecutor<
2325
constructor(public readonly astNode: T) {}
2426

2527
isValid(
26-
value: InternalValueRepresentation,
28+
value: InternalValidValueRepresentation | InternalErrorValueRepresentation,
2729
context: ExecutionContext,
2830
attribute: T extends ValueTypeConstraintInlineDefinition
2931
? ValueTypeAttribute
@@ -42,8 +44,12 @@ export class ConstraintExecutor<
4244
context.evaluationContext,
4345
context.wrapperFactories,
4446
);
47+
if (ERROR_TYPEGUARD(result)) {
48+
context.logger.logErr(result.toString());
49+
return false;
50+
}
4551
assert(
46-
context.valueTypeProvider.Primitives.Boolean.isInternalValueRepresentation(
52+
context.valueTypeProvider.Primitives.Boolean.isInternalValidValueRepresentation(
4753
result,
4854
),
4955
);

libs/execution/src/lib/debugging/debug-log-visitor.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: AGPL-3.0-only
44

55
import {
6+
ERROR_TYPEGUARD,
67
type WrapperFactoryProvider,
78
internalValueToString,
89
} from '@jvalue/jayvee-language-server';
@@ -57,7 +58,12 @@ export class DebugLogVisitor implements IoTypeVisitor<void> {
5758

5859
const row = table.getRow(i);
5960
const rowData = [...row.values()]
60-
.map((cell) => internalValueToString(cell, this.wrapperFactories))
61+
.map((cell) => {
62+
if (ERROR_TYPEGUARD(cell)) {
63+
return cell.name;
64+
}
65+
return internalValueToString(cell, this.wrapperFactories);
66+
})
6167
.join(' | ');
6268
this.log(`[Row ${i}] ${rowData}`);
6369
}

libs/execution/src/lib/execution-context.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import { inspect } from 'node:util';
99
import {
1010
type BlockDefinition,
1111
type ConstraintDefinition,
12+
ERROR_TYPEGUARD,
1213
type EvaluationContext,
13-
type InternalValueRepresentation,
14+
type InternalValidValueRepresentation,
1415
type PipelineDefinition,
1516
type PropertyAssignment,
1617
type TransformDefinition,
@@ -94,7 +95,7 @@ export class ExecutionContext {
9495
this.logger.setLoggingContext(this.getCurrentNode().name);
9596
}
9697

97-
public getPropertyValue<I extends InternalValueRepresentation>(
98+
public getPropertyValue<I extends InternalValidValueRepresentation>(
9899
propertyName: string,
99100
valueType: ValueType<I>,
100101
): I {
@@ -110,7 +111,7 @@ export class ExecutionContext {
110111
this.wrapperFactories,
111112
valueType,
112113
);
113-
assert(propertyValue !== undefined);
114+
assert(!ERROR_TYPEGUARD(propertyValue));
114115
return propertyValue;
115116
}
116117

@@ -172,7 +173,7 @@ export class ExecutionContext {
172173
}
173174
}
174175

175-
private getDefaultPropertyValue<I extends InternalValueRepresentation>(
176+
private getDefaultPropertyValue<I extends InternalValidValueRepresentation>(
176177
propertyName: string,
177178
valueType: ValueType<I>,
178179
): I {
@@ -182,7 +183,7 @@ export class ExecutionContext {
182183

183184
const defaultValue = propertySpec.defaultValue;
184185
assert(defaultValue !== undefined);
185-
assert(valueType.isInternalValueRepresentation(defaultValue));
186+
assert(valueType.isInternalValidValueRepresentation(defaultValue));
186187

187188
return defaultValue;
188189
}

libs/execution/src/lib/transforms/transform-executor.spec.ts

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import assert from 'assert';
77
import path from 'node:path';
88

99
import {
10-
type InternalValueRepresentation,
10+
ERROR_TYPEGUARD,
11+
type InternalValidValueRepresentation,
12+
InvalidValue,
1113
type JayveeServices,
1214
type TransformDefinition,
1315
createJayveeServices,
@@ -33,6 +35,10 @@ import { type Table, type TableColumn } from '../types/io-types/table';
3335

3436
import { type PortDetails, TransformExecutor } from './transform-executor';
3537

38+
function expectNoErrorsInColumn(column: TableColumn) {
39+
expect(column.values.every((value) => ERROR_TYPEGUARD(value)));
40+
}
41+
3642
describe('Validation of TransformExecutor', () => {
3743
let parse: (
3844
input: string,
@@ -72,10 +78,7 @@ describe('Validation of TransformExecutor', () => {
7278
input: string,
7379
inputTable: Table,
7480
columnNames: string[],
75-
): Promise<{
76-
resultingColumn: TableColumn<InternalValueRepresentation>;
77-
rowsToDelete: number[];
78-
}> {
81+
): Promise<TableColumn<InternalValidValueRepresentation>> {
7982
const document = await parse(input, { validation: true });
8083
expectNoParserAndLexerErrors(document);
8184

@@ -147,15 +150,15 @@ describe('Validation of TransformExecutor', () => {
147150
transformColumnNames,
148151
);
149152

150-
expect(result.rowsToDelete).toHaveLength(0);
151-
expect(result.resultingColumn.valueType).toEqual(
153+
expectNoErrorsInColumn(result);
154+
expect(result.valueType).toEqual(
152155
services.ValueTypeProvider.Primitives.Integer,
153156
);
154-
expect(result.resultingColumn.values).toHaveLength(1);
155-
expect(result.resultingColumn.values).toEqual(expect.arrayContaining([21]));
157+
expect(result.values).toHaveLength(1);
158+
expect(result.values).toEqual(expect.arrayContaining([21]));
156159
});
157160

158-
it('should diagnose no error on invalid value representation', async () => {
161+
it('should evaluate InvalidValue on invalid value representation', async () => {
159162
const text = readJvTestAsset(
160163
'transform-executor/invalid-input-output-type-transform.jv',
161164
);
@@ -187,12 +190,13 @@ describe('Validation of TransformExecutor', () => {
187190
transformColumnNames,
188191
);
189192

190-
expect(result.rowsToDelete).toHaveLength(1);
191-
expect(result.rowsToDelete).toEqual(expect.arrayContaining([0]));
192-
expect(result.resultingColumn.valueType).toEqual(
193+
expect(result.valueType).toEqual(
193194
services.ValueTypeProvider.Primitives.Text,
194195
);
195-
expect(result.resultingColumn.values).toHaveLength(0);
196+
expect(result.values).toHaveLength(1);
197+
const [value] = result.values;
198+
assert(value !== undefined);
199+
expect(value).toBeInstanceOf(InvalidValue);
196200
});
197201

198202
it('should diagnose no error on valid value', async () => {
@@ -234,14 +238,12 @@ describe('Validation of TransformExecutor', () => {
234238
transformColumnNames,
235239
);
236240

237-
expect(result.rowsToDelete).toHaveLength(0);
238-
expect(result.resultingColumn.valueType).toEqual(
241+
expectNoErrorsInColumn(result);
242+
expect(result.valueType).toEqual(
239243
services.ValueTypeProvider.Primitives.Integer,
240244
);
241-
expect(result.resultingColumn.values).toHaveLength(1);
242-
expect(result.resultingColumn.values).toEqual(
243-
expect.arrayContaining([106]),
244-
);
245+
expect(result.values).toHaveLength(1);
246+
expect(result.values).toEqual(expect.arrayContaining([106]));
245247
});
246248

247249
it('should diagnose error on empty columns map', async () => {
@@ -287,7 +289,7 @@ describe('Validation of TransformExecutor', () => {
287289
}
288290
});
289291

290-
it('should diagnose no error on invalid column type', async () => {
292+
it('should evaluate InvalidValue on invalid column type', async () => {
291293
const text = readJvTestAsset(
292294
'transform-executor/valid-decimal-integer-transform.jv',
293295
);
@@ -319,14 +321,16 @@ describe('Validation of TransformExecutor', () => {
319321
transformColumnNames,
320322
);
321323

322-
expect(result.rowsToDelete).toHaveLength(1);
323-
expect(result.resultingColumn.valueType).toEqual(
324+
expect(result.valueType).toEqual(
324325
services.ValueTypeProvider.Primitives.Integer,
325326
);
326-
expect(result.resultingColumn.values).toHaveLength(0);
327+
expect(result.values).toHaveLength(1);
328+
const [value] = result.values;
329+
assert(value !== undefined);
330+
expect(value).toBeInstanceOf(InvalidValue);
327331
});
328332

329-
it('should diagnose no error on invalid row value', async () => {
333+
it('should evaluate InvalidValue on invalid row value', async () => {
330334
const text = readJvTestAsset(
331335
'transform-executor/valid-decimal-integer-transform.jv',
332336
);
@@ -358,15 +362,19 @@ describe('Validation of TransformExecutor', () => {
358362
transformColumnNames,
359363
);
360364

361-
expect(result.rowsToDelete).toHaveLength(1);
362-
expect(result.resultingColumn.valueType).toEqual(
365+
expect(result.valueType).toEqual(
363366
services.ValueTypeProvider.Primitives.Integer,
364367
);
365-
expect(result.resultingColumn.values).toHaveLength(1);
366-
expect(result.resultingColumn.values).toEqual(expect.arrayContaining([21]));
368+
expect(result.values).toHaveLength(2);
369+
const [a, b] = result.values;
370+
assert(a !== undefined);
371+
assert(b !== undefined);
372+
expect(a).toBeInstanceOf(InvalidValue);
373+
expect(b).toBe(21);
374+
expect(result.values).toEqual(expect.arrayContaining([21]));
367375
});
368376

369-
it('should diagnose no error on expression evaluation error', async () => {
377+
it('should evaluate InvalidValue on an erroneous expression', async () => {
370378
const text = readJvTestAsset(
371379
'transform-executor/invalid-expression-evaluation-error.jv',
372380
);
@@ -405,10 +413,12 @@ describe('Validation of TransformExecutor', () => {
405413
transformColumnNames,
406414
);
407415

408-
expect(result.rowsToDelete).toHaveLength(1);
409-
expect(result.resultingColumn.valueType).toEqual(
416+
expect(result.valueType).toEqual(
410417
services.ValueTypeProvider.Primitives.Decimal,
411418
);
412-
expect(result.resultingColumn.values).toHaveLength(0);
419+
expect(result.values).toHaveLength(1);
420+
const [value] = result.values;
421+
assert(value !== undefined);
422+
expect(value).toBeInstanceOf(InvalidValue);
413423
});
414424
});

0 commit comments

Comments
 (0)