Teach package runtime edge endpoints
This commit is contained in:
Vendored
+86
-16
@@ -173,7 +173,8 @@ const cardinalityFromOption = (value) => {
|
||||
normalized === "exactly_one" ||
|
||||
normalized === "many" ||
|
||||
normalized === "many_unique" ||
|
||||
normalized === "many_ordered") {
|
||||
normalized === "many_ordered" ||
|
||||
normalized === "many_unique_ordered") {
|
||||
return normalized;
|
||||
}
|
||||
throw new Error(`Unknown Camino cardinality: ${String(value)}`);
|
||||
@@ -303,6 +304,49 @@ const fieldSchemaFromField = (field, schemaVersion) => {
|
||||
isDisplayLabel: getReflectionOption(field, DISPLAY_LABEL_OPTION) === true,
|
||||
};
|
||||
};
|
||||
const isManyCardinality = (cardinality) => cardinality === "many" ||
|
||||
cardinality === "many_unique" ||
|
||||
cardinality === "many_ordered" ||
|
||||
cardinality === "many_unique_ordered";
|
||||
const isOrderedCardinality = (cardinality) => cardinality === "many_ordered" || cardinality === "many_unique_ordered";
|
||||
const endpointFromOption = (params) => {
|
||||
const object = asObject(params.option);
|
||||
const classRef = asObject(object?.class)
|
||||
? symbolRefFromOption(object?.class, {
|
||||
namespace: "",
|
||||
name: "",
|
||||
version: params.fallbackVersion,
|
||||
})
|
||||
: params.fallbackClass;
|
||||
const interfaceRef = asObject(object?.interface)
|
||||
? symbolRefFromOption(object?.interface, {
|
||||
namespace: "",
|
||||
name: "",
|
||||
version: params.fallbackVersion,
|
||||
})
|
||||
: undefined;
|
||||
const projection = readString(object, "projection") ?? params.fallbackProjection;
|
||||
const cardinality = object?.cardinality === undefined
|
||||
? params.fallbackCardinality
|
||||
: cardinalityFromOption(object.cardinality);
|
||||
if (!classRef?.name && !interfaceRef?.name) {
|
||||
throw new Error(`Edge endpoint ${projection} requires class or interface`);
|
||||
}
|
||||
return {
|
||||
...(classRef?.name ? { class: classRef } : {}),
|
||||
...(interfaceRef?.name ? { interface: interfaceRef } : {}),
|
||||
projection,
|
||||
cardinality,
|
||||
indexed: readBoolean(object, "indexed") ?? false,
|
||||
};
|
||||
};
|
||||
const symbolArrayFromOption = (value, defaults) => asArray(value)
|
||||
.map((item) => symbolRefFromOption(item, {
|
||||
namespace: defaults.schemaNamespace,
|
||||
name: "",
|
||||
version: defaults.schemaVersion,
|
||||
}))
|
||||
.filter((symbol) => Boolean(symbol.name));
|
||||
const isStringType = (type) => type.protoType === "string" || type.protoType === "google.protobuf.StringValue";
|
||||
const validateDisplayLabelFields = (type, fields, operationServices) => {
|
||||
const displayLabelFields = fields.filter((field) => field.isDisplayLabel);
|
||||
@@ -397,45 +441,71 @@ const classSchemaFromType = (type, sourceFile, defaults, operationServices) => {
|
||||
if (!edgeOption) {
|
||||
return [];
|
||||
}
|
||||
const targetClass = symbolRefFromOption(edgeOption.target, {
|
||||
const legacyTargetClass = symbolRefFromOption(edgeOption.target, {
|
||||
namespace: defaults.schemaNamespace,
|
||||
name: "",
|
||||
version: defaults.schemaVersion,
|
||||
});
|
||||
if (!targetClass.name) {
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} requires target`);
|
||||
}
|
||||
const cardinality = cardinalityFromOption(edgeOption.cardinality);
|
||||
const fromEndpoint = endpointFromOption({
|
||||
option: edgeOption.this_endpoint,
|
||||
fallbackClass: classId,
|
||||
fallbackProjection: field.name,
|
||||
fallbackCardinality: cardinality,
|
||||
fallbackVersion: defaults.schemaVersion,
|
||||
});
|
||||
const toEndpoint = endpointFromOption({
|
||||
option: edgeOption.other_endpoint,
|
||||
fallbackClass: legacyTargetClass.name ? legacyTargetClass : undefined,
|
||||
fallbackProjection: readString(edgeOption, "inverse") ?? "",
|
||||
fallbackCardinality: "many",
|
||||
fallbackVersion: defaults.schemaVersion,
|
||||
});
|
||||
if (!toEndpoint.projection) {
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} requires other_endpoint.projection or inverse`);
|
||||
}
|
||||
const sourceType = typeRefForField(field, defaults.schemaVersion);
|
||||
if (sourceType.symbol?.namespace !== "camino" ||
|
||||
sourceType.symbol.name !== "Ref") {
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} must use camino.Ref`);
|
||||
}
|
||||
const isMany = cardinality === "many" ||
|
||||
cardinality === "many_unique" ||
|
||||
cardinality === "many_ordered";
|
||||
const isMany = isManyCardinality(fromEndpoint.cardinality);
|
||||
if (isMany && !field.repeated) {
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} must be repeated for ${cardinality}`);
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} must be repeated for ${fromEndpoint.cardinality}`);
|
||||
}
|
||||
if (!isMany && field.repeated) {
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} must not be repeated for ${cardinality}`);
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} must not be repeated for ${fromEndpoint.cardinality}`);
|
||||
}
|
||||
if (isOrderedCardinality(fromEndpoint.cardinality) &&
|
||||
isOrderedCardinality(toEndpoint.cardinality)) {
|
||||
throw new Error(`Edge field ${type.fullName}.${field.name} cannot be ordered on both endpoints yet`);
|
||||
}
|
||||
const edgeId = symbolRefFromOption(edgeOption.id, {
|
||||
namespace: defaults.schemaNamespace,
|
||||
name: `${type.name}.${field.name}`,
|
||||
version: defaults.schemaVersion,
|
||||
});
|
||||
const fallbackToClass = toEndpoint.class ?? {
|
||||
namespace: "",
|
||||
name: "",
|
||||
version: "",
|
||||
};
|
||||
return [
|
||||
{
|
||||
id: edgeId,
|
||||
sourceField: field.name,
|
||||
fromClass: classId,
|
||||
toClass: targetClass,
|
||||
cardinality,
|
||||
...(readString(edgeOption, "inverse")
|
||||
? { inverse: readString(edgeOption, "inverse") }
|
||||
sourceField: fromEndpoint.projection,
|
||||
fromClass: fromEndpoint.class ?? classId,
|
||||
toClass: fallbackToClass,
|
||||
cardinality: fromEndpoint.cardinality,
|
||||
...(toEndpoint.projection
|
||||
? { inverse: toEndpoint.projection }
|
||||
: {}),
|
||||
fields: [],
|
||||
fromEndpoint,
|
||||
toEndpoint,
|
||||
declaringClass: classId,
|
||||
tags: symbolArrayFromOption(edgeOption.tag, defaults),
|
||||
implements: symbolArrayFromOption(edgeOption.implements, defaults),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user