@highstate/backend-api 0.28.0 → 0.29.0
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/dist/highstate.manifest.json +1 -1
- package/dist/index.js +23 -25
- package/package.json +1 -1
- package/src/shared/conversion.ts +28 -35
- package/src/shared/error-handling.test.ts +23 -0
package/dist/index.js
CHANGED
|
@@ -235,7 +235,6 @@ function toInstance(instance) {
|
|
|
235
235
|
])),
|
|
236
236
|
injectionInputs: (instance.injectionInputs ?? []).map((value) => ({ hubId: value.hubId })),
|
|
237
237
|
position: instance.position ?? undefined,
|
|
238
|
-
resolvedInputs: toInstanceReferenceMap(instance.resolvedInputs),
|
|
239
238
|
parentId: instance.parentId,
|
|
240
239
|
outputs: toInstanceReferenceMap(instance.outputs),
|
|
241
240
|
resolvedOutputs: toInstanceReferenceMap(instance.resolvedOutputs)
|
|
@@ -336,7 +335,6 @@ function toHubPatch(hub, paths) {
|
|
|
336
335
|
}
|
|
337
336
|
function toInstanceState(state) {
|
|
338
337
|
const model = state.model ? instanceModelSchema.parse(state.model) : undefined;
|
|
339
|
-
const resolvedInputs = state.resolvedInputs ? fromUnknownInstanceReferenceMap(state.resolvedInputs) : undefined;
|
|
340
338
|
return create2(InstanceStateSchema, {
|
|
341
339
|
id: state.id,
|
|
342
340
|
instanceId: state.instanceId,
|
|
@@ -357,7 +355,6 @@ function toInstanceState(state) {
|
|
|
357
355
|
currentResourceCount: state.lastOperationState.currentResourceCount ?? undefined,
|
|
358
356
|
totalResourceCount: state.lastOperationState.totalResourceCount ?? undefined,
|
|
359
357
|
model: toInstance(instanceModelSchema.parse(state.lastOperationState.model)),
|
|
360
|
-
resolvedInputs: toInstanceReferenceMap(fromUnknownInstanceReferenceMap(state.lastOperationState.resolvedInputs)),
|
|
361
358
|
startedAt: toNullableTimestamp(state.lastOperationState.startedAt),
|
|
362
359
|
finishedAt: toNullableTimestamp(state.lastOperationState.finishedAt)
|
|
363
360
|
} : undefined,
|
|
@@ -369,8 +366,7 @@ function toInstanceState(state) {
|
|
|
369
366
|
currentResourceCount: state.currentResourceCount ?? undefined,
|
|
370
367
|
hasResourceHooks: state.hasResourceHooks,
|
|
371
368
|
statusFields: state.statusFields ? fromJson(ValueSchema, jsonValueSchema.parse(state.statusFields)) : undefined,
|
|
372
|
-
model: model ? toInstance(model) : undefined
|
|
373
|
-
resolvedInputs: toInstanceReferenceMap(resolvedInputs)
|
|
369
|
+
model: model ? toInstance(model) : undefined
|
|
374
370
|
});
|
|
375
371
|
}
|
|
376
372
|
function toInstanceCustomStatus(status) {
|
|
@@ -446,22 +442,28 @@ function toLibrary(library) {
|
|
|
446
442
|
});
|
|
447
443
|
}
|
|
448
444
|
function toComponent(component) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
name,
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
445
|
+
try {
|
|
446
|
+
return create2(ComponentSchema, {
|
|
447
|
+
type: component.type,
|
|
448
|
+
kind: toComponentKind(component.kind),
|
|
449
|
+
arguments: Object.fromEntries(Object.entries(component.args).map(([name, argument]) => [
|
|
450
|
+
name,
|
|
451
|
+
{
|
|
452
|
+
schema: jsonObjectSchema.parse(argument.schema),
|
|
453
|
+
required: argument.required,
|
|
454
|
+
meta: argument.meta
|
|
455
|
+
}
|
|
456
|
+
])),
|
|
457
|
+
inputs: Object.fromEntries(Object.entries(component.inputs).map(([name, port]) => [name, toComponentPort(port)])),
|
|
458
|
+
outputs: Object.fromEntries(Object.entries(component.outputs).map(([name, port]) => [name, toComponentPort(port)])),
|
|
459
|
+
meta: component.meta,
|
|
460
|
+
definitionHash: component.definitionHash
|
|
461
|
+
});
|
|
462
|
+
} catch (error) {
|
|
463
|
+
throw new Error(`Failed to convert component "${component.type}" to an API message`, {
|
|
464
|
+
cause: error
|
|
465
|
+
});
|
|
466
|
+
}
|
|
465
467
|
}
|
|
466
468
|
function toEntity(entity) {
|
|
467
469
|
return create2(EntitySchema, {
|
|
@@ -470,7 +472,6 @@ function toEntity(entity) {
|
|
|
470
472
|
directExtensions: entity.directExtensions ?? [],
|
|
471
473
|
inclusions: entity.inclusions ?? [],
|
|
472
474
|
directInclusions: entity.directInclusions ?? [],
|
|
473
|
-
schema: jsonObjectSchema.parse(entity.schema),
|
|
474
475
|
meta: entity.meta,
|
|
475
476
|
definitionHash: entity.definitionHash
|
|
476
477
|
});
|
|
@@ -504,9 +505,6 @@ function toInstanceReferenceMap(values) {
|
|
|
504
505
|
function fromInstanceReferenceMap(values) {
|
|
505
506
|
return Object.fromEntries(Object.entries(values).map(([key, list]) => [key, list.values.map(fromInstanceReference)]));
|
|
506
507
|
}
|
|
507
|
-
function fromUnknownInstanceReferenceMap(value) {
|
|
508
|
-
return instanceModelSchema.shape.inputs.unwrap().parse(value);
|
|
509
|
-
}
|
|
510
508
|
function fromInstanceReference(value) {
|
|
511
509
|
return instanceInputSchema.parse({
|
|
512
510
|
instanceId: value.instanceId,
|
package/package.json
CHANGED
package/src/shared/conversion.ts
CHANGED
|
@@ -113,7 +113,6 @@ export function toInstance(instance: InstanceModel): Instance {
|
|
|
113
113
|
),
|
|
114
114
|
injectionInputs: (instance.injectionInputs ?? []).map(value => ({ hubId: value.hubId })),
|
|
115
115
|
position: instance.position ?? undefined,
|
|
116
|
-
resolvedInputs: toInstanceReferenceMap(instance.resolvedInputs),
|
|
117
116
|
parentId: instance.parentId,
|
|
118
117
|
outputs: toInstanceReferenceMap(instance.outputs),
|
|
119
118
|
resolvedOutputs: toInstanceReferenceMap(instance.resolvedOutputs),
|
|
@@ -234,9 +233,6 @@ export function toHubPatch(hub: Hub, paths: readonly string[]): HubModelPatch {
|
|
|
234
233
|
|
|
235
234
|
export function toInstanceState(state: BackendInstanceState): InstanceState {
|
|
236
235
|
const model = state.model ? instanceModelSchema.parse(state.model) : undefined
|
|
237
|
-
const resolvedInputs = state.resolvedInputs
|
|
238
|
-
? fromUnknownInstanceReferenceMap(state.resolvedInputs)
|
|
239
|
-
: undefined
|
|
240
236
|
|
|
241
237
|
return create(InstanceStateSchema, {
|
|
242
238
|
id: state.id,
|
|
@@ -263,9 +259,6 @@ export function toInstanceState(state: BackendInstanceState): InstanceState {
|
|
|
263
259
|
currentResourceCount: state.lastOperationState.currentResourceCount ?? undefined,
|
|
264
260
|
totalResourceCount: state.lastOperationState.totalResourceCount ?? undefined,
|
|
265
261
|
model: toInstance(instanceModelSchema.parse(state.lastOperationState.model)),
|
|
266
|
-
resolvedInputs: toInstanceReferenceMap(
|
|
267
|
-
fromUnknownInstanceReferenceMap(state.lastOperationState.resolvedInputs),
|
|
268
|
-
),
|
|
269
262
|
startedAt: toNullableTimestamp(state.lastOperationState.startedAt),
|
|
270
263
|
finishedAt: toNullableTimestamp(state.lastOperationState.finishedAt),
|
|
271
264
|
}
|
|
@@ -281,7 +274,6 @@ export function toInstanceState(state: BackendInstanceState): InstanceState {
|
|
|
281
274
|
? fromJson(ValueSchema, jsonValueSchema.parse(state.statusFields))
|
|
282
275
|
: undefined,
|
|
283
276
|
model: model ? toInstance(model) : undefined,
|
|
284
|
-
resolvedInputs: toInstanceReferenceMap(resolvedInputs),
|
|
285
277
|
})
|
|
286
278
|
}
|
|
287
279
|
|
|
@@ -378,28 +370,34 @@ export function toLibrary(library: {
|
|
|
378
370
|
}
|
|
379
371
|
|
|
380
372
|
export function toComponent(component: ComponentModel): Component {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
Object.
|
|
386
|
-
name,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
Object.
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
Object.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
373
|
+
try {
|
|
374
|
+
return create(ComponentSchema, {
|
|
375
|
+
type: component.type,
|
|
376
|
+
kind: toComponentKind(component.kind),
|
|
377
|
+
arguments: Object.fromEntries(
|
|
378
|
+
Object.entries(component.args).map(([name, argument]) => [
|
|
379
|
+
name,
|
|
380
|
+
{
|
|
381
|
+
schema: jsonObjectSchema.parse(argument.schema),
|
|
382
|
+
required: argument.required,
|
|
383
|
+
meta: argument.meta,
|
|
384
|
+
},
|
|
385
|
+
]),
|
|
386
|
+
),
|
|
387
|
+
inputs: Object.fromEntries(
|
|
388
|
+
Object.entries(component.inputs).map(([name, port]) => [name, toComponentPort(port)]),
|
|
389
|
+
),
|
|
390
|
+
outputs: Object.fromEntries(
|
|
391
|
+
Object.entries(component.outputs).map(([name, port]) => [name, toComponentPort(port)]),
|
|
392
|
+
),
|
|
393
|
+
meta: component.meta,
|
|
394
|
+
definitionHash: component.definitionHash,
|
|
395
|
+
})
|
|
396
|
+
} catch (error) {
|
|
397
|
+
throw new Error(`Failed to convert component "${component.type}" to an API message`, {
|
|
398
|
+
cause: error,
|
|
399
|
+
})
|
|
400
|
+
}
|
|
403
401
|
}
|
|
404
402
|
|
|
405
403
|
export function toEntity(entity: EntityModel): Entity {
|
|
@@ -409,7 +407,6 @@ export function toEntity(entity: EntityModel): Entity {
|
|
|
409
407
|
directExtensions: entity.directExtensions ?? [],
|
|
410
408
|
inclusions: entity.inclusions ?? [],
|
|
411
409
|
directInclusions: entity.directInclusions ?? [],
|
|
412
|
-
schema: jsonObjectSchema.parse(entity.schema),
|
|
413
410
|
meta: entity.meta,
|
|
414
411
|
definitionHash: entity.definitionHash,
|
|
415
412
|
})
|
|
@@ -456,10 +453,6 @@ function fromInstanceReferenceMap(
|
|
|
456
453
|
)
|
|
457
454
|
}
|
|
458
455
|
|
|
459
|
-
function fromUnknownInstanceReferenceMap(value: unknown): NonNullable<InstanceModel["inputs"]> {
|
|
460
|
-
return instanceModelSchema.shape.inputs.unwrap().parse(value)
|
|
461
|
-
}
|
|
462
|
-
|
|
463
456
|
function fromInstanceReference(value: Instance["inputs"][string]["values"][number]) {
|
|
464
457
|
return instanceInputSchema.parse({
|
|
465
458
|
instanceId: value.instanceId,
|
|
@@ -3,6 +3,7 @@ import { BadRequestSchema, ErrorInfoSchema } from "@highstate/api/v1"
|
|
|
3
3
|
import {
|
|
4
4
|
BackendError,
|
|
5
5
|
BackendErrorCategory,
|
|
6
|
+
InvalidOperationPlanError,
|
|
6
7
|
InvalidPageSizeError,
|
|
7
8
|
PermissionDeniedError,
|
|
8
9
|
} from "@highstate/backend/shared"
|
|
@@ -35,6 +36,28 @@ describe("createErrorHandlingInterceptor", () => {
|
|
|
35
36
|
}
|
|
36
37
|
})
|
|
37
38
|
|
|
39
|
+
it("exposes operation planning validation errors", async () => {
|
|
40
|
+
const logger = { error: vi.fn() }
|
|
41
|
+
const interceptor = createErrorHandlingInterceptor({ logger } as never)
|
|
42
|
+
const handler = interceptor(async () => {
|
|
43
|
+
throw new InvalidOperationPlanError(
|
|
44
|
+
"Operation options are invalid: ghost options are supported only for updates",
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const result = (await handler({
|
|
49
|
+
method: { name: "PlanOperation" },
|
|
50
|
+
header: new Headers(),
|
|
51
|
+
} as never).catch(error => error as ConnectError)) as ConnectError
|
|
52
|
+
|
|
53
|
+
expect(result.code).toBe(Code.InvalidArgument)
|
|
54
|
+
expect(result.rawMessage).toBe(
|
|
55
|
+
"Operation options are invalid: ghost options are supported only for updates",
|
|
56
|
+
)
|
|
57
|
+
expect(result.findDetails(ErrorInfoSchema)[0]?.reason).toBe("OPERATION_PLAN_INVALID")
|
|
58
|
+
expect(logger.error).not.toHaveBeenCalled()
|
|
59
|
+
})
|
|
60
|
+
|
|
38
61
|
it("sanitizes and logs unexpected errors once", async () => {
|
|
39
62
|
const logger = { error: vi.fn() }
|
|
40
63
|
const interceptor = createErrorHandlingInterceptor({ logger } as never)
|