fix(ui): fix enum parsing for optional enums

Closes #5121

- Parse `anyOf` for enums (present when they are optional)
- Consolidate `FieldTypeParseError` and `UnsupportedFieldTypeError` into `FieldParseError` (there was no difference in handling and it simplifies things a bit)
This commit is contained in:
psychedelicious 2023-11-30 22:01:33 +11:00 committed by Kent Keirsey
parent 3e01c396e1
commit a6d4e4ed57
5 changed files with 51 additions and 53 deletions

View File

@ -978,6 +978,7 @@
"unsupportedAnyOfLength": "too many union members ({{count}})",
"unsupportedMismatchedUnion": "mismatched CollectionOrScalar type with base types {{firstType}} and {{secondType}}",
"unableToParseFieldType": "unable to parse field type",
"unableToExtractEnumOptions": "unable to extract enum options",
"uNetField": "UNet",
"uNetFieldDescription": "UNet submodel.",
"unhandledInputProperty": "Unhandled input property",

View File

@ -43,10 +43,10 @@ export class NodeUpdateError extends Error {
}
/**
* FieldTypeParseError
* FieldParseError
* Raised when a field cannot be parsed from a field schema.
*/
export class FieldTypeParseError extends Error {
export class FieldParseError extends Error {
/**
* Create FieldTypeParseError
* @param {String} message
@ -56,18 +56,3 @@ export class FieldTypeParseError extends Error {
this.name = this.constructor.name;
}
}
/**
* UnsupportedFieldTypeError
* Raised when an unsupported field type is parsed.
*/
export class UnsupportedFieldTypeError extends Error {
/**
* Create UnsupportedFieldTypeError
* @param {String} message
*/
constructor(message: string) {
super(message);
this.name = this.constructor.name;
}
}

View File

@ -23,7 +23,12 @@ import {
VAEModelFieldInputTemplate,
isStatefulFieldType,
} from 'features/nodes/types/field';
import { InvocationFieldSchema } from 'features/nodes/types/openapi';
import {
InvocationFieldSchema,
isSchemaObject,
} from 'features/nodes/types/openapi';
import { t } from 'i18next';
import { FieldParseError } from 'features/nodes/types/error';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type FieldInputTemplateBuilder<T extends FieldInputTemplate = any> = // valid `any`!
@ -321,7 +326,28 @@ const buildImageFieldInputTemplate: FieldInputTemplateBuilder<
const buildEnumFieldInputTemplate: FieldInputTemplateBuilder<
EnumFieldInputTemplate
> = ({ schemaObject, baseField, isCollection, isCollectionOrScalar }) => {
const options = schemaObject.enum ?? [];
let options: EnumFieldInputTemplate['options'] = [];
if (schemaObject.anyOf) {
const filteredAnyOf = schemaObject.anyOf.filter((i) => {
if (isSchemaObject(i)) {
if (i.type === 'null') {
return false;
}
}
return true;
});
const firstAnyOf = filteredAnyOf[0];
if (filteredAnyOf.length !== 1 || !isSchemaObject(firstAnyOf)) {
options = [];
} else {
options = firstAnyOf.enum ?? [];
}
} else {
options = schemaObject.enum ?? [];
}
if (options.length === 0) {
throw new FieldParseError(t('nodes.unableToExtractEnumOptions'));
}
const template: EnumFieldInputTemplate = {
...baseField,
type: {

View File

@ -1,10 +1,4 @@
import { t } from 'i18next';
import { isArray } from 'lodash-es';
import { OpenAPIV3_1 } from 'openapi-types';
import {
FieldTypeParseError,
UnsupportedFieldTypeError,
} from 'features/nodes/types/error';
import { FieldParseError } from 'features/nodes/types/error';
import { FieldType } from 'features/nodes/types/field';
import {
OpenAPIV3_1SchemaOrRef,
@ -14,6 +8,9 @@ import {
isRefObject,
isSchemaObject,
} from 'features/nodes/types/openapi';
import { t } from 'i18next';
import { isArray } from 'lodash-es';
import { OpenAPIV3_1 } from 'openapi-types';
/**
* Transforms an invocation output ref object to field type.
@ -70,7 +67,7 @@ export const parseFieldType = (
// This is a single ref type
const name = refObjectToSchemaName(allOf[0]);
if (!name) {
throw new FieldTypeParseError(
throw new FieldParseError(
t('nodes.unableToExtractSchemaNameFromRef')
);
}
@ -95,7 +92,7 @@ export const parseFieldType = (
if (isRefObject(filteredAnyOf[0])) {
const name = refObjectToSchemaName(filteredAnyOf[0]);
if (!name) {
throw new FieldTypeParseError(
throw new FieldParseError(
t('nodes.unableToExtractSchemaNameFromRef')
);
}
@ -120,7 +117,7 @@ export const parseFieldType = (
if (filteredAnyOf.length !== 2) {
// This is a union of more than 2 types, which we don't support
throw new UnsupportedFieldTypeError(
throw new FieldParseError(
t('nodes.unsupportedAnyOfLength', {
count: filteredAnyOf.length,
})
@ -167,7 +164,7 @@ export const parseFieldType = (
};
}
throw new UnsupportedFieldTypeError(
throw new FieldParseError(
t('nodes.unsupportedMismatchedUnion', {
firstType,
secondType,
@ -186,7 +183,7 @@ export const parseFieldType = (
if (isSchemaObject(schemaObject.items)) {
const itemType = schemaObject.items.type;
if (!itemType || isArray(itemType)) {
throw new UnsupportedFieldTypeError(
throw new FieldParseError(
t('nodes.unsupportedArrayItemType', {
type: itemType,
})
@ -196,7 +193,7 @@ export const parseFieldType = (
const name = OPENAPI_TO_FIELD_TYPE_MAP[itemType];
if (!name) {
// it's 'null', 'object', or 'array' - skip
throw new UnsupportedFieldTypeError(
throw new FieldParseError(
t('nodes.unsupportedArrayItemType', {
type: itemType,
})
@ -212,7 +209,7 @@ export const parseFieldType = (
// This is a ref object, extract the type name
const name = refObjectToSchemaName(schemaObject.items);
if (!name) {
throw new FieldTypeParseError(
throw new FieldParseError(
t('nodes.unableToExtractSchemaNameFromRef')
);
}
@ -226,7 +223,7 @@ export const parseFieldType = (
const name = OPENAPI_TO_FIELD_TYPE_MAP[schemaObject.type];
if (!name) {
// it's 'null', 'object', or 'array' - skip
throw new UnsupportedFieldTypeError(
throw new FieldParseError(
t('nodes.unsupportedArrayItemType', {
type: schemaObject.type,
})
@ -242,9 +239,7 @@ export const parseFieldType = (
} else if (isRefObject(schemaObject)) {
const name = refObjectToSchemaName(schemaObject);
if (!name) {
throw new FieldTypeParseError(
t('nodes.unableToExtractSchemaNameFromRef')
);
throw new FieldParseError(t('nodes.unableToExtractSchemaNameFromRef'));
}
return {
name,
@ -252,5 +247,5 @@ export const parseFieldType = (
isCollectionOrScalar: false,
};
}
throw new FieldTypeParseError(t('nodes.unableToParseFieldType'));
throw new FieldParseError(t('nodes.unableToParseFieldType'));
};

View File

@ -1,12 +1,6 @@
import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { t } from 'i18next';
import { reduce } from 'lodash-es';
import { OpenAPIV3_1 } from 'openapi-types';
import {
FieldTypeParseError,
UnsupportedFieldTypeError,
} from 'features/nodes/types/error';
import { FieldParseError } from 'features/nodes/types/error';
import {
FieldInputTemplate,
FieldOutputTemplate,
@ -18,6 +12,9 @@ import {
isInvocationOutputSchemaObject,
isInvocationSchemaObject,
} from 'features/nodes/types/openapi';
import { t } from 'i18next';
import { reduce } from 'lodash-es';
import { OpenAPIV3_1 } from 'openapi-types';
import { buildFieldInputTemplate } from './buildFieldInputTemplate';
import { buildFieldOutputTemplate } from './buildFieldOutputTemplate';
import { parseFieldType } from './parseFieldType';
@ -133,10 +130,7 @@ export const parseSchema = (
inputsAccumulator[propertyName] = fieldInputTemplate;
} catch (e) {
if (
e instanceof FieldTypeParseError ||
e instanceof UnsupportedFieldTypeError
) {
if (e instanceof FieldParseError) {
logger('nodes').warn(
{
node: type,
@ -225,10 +219,7 @@ export const parseSchema = (
outputsAccumulator[propertyName] = fieldOutputTemplate;
} catch (e) {
if (
e instanceof FieldTypeParseError ||
e instanceof UnsupportedFieldTypeError
) {
if (e instanceof FieldParseError) {
logger('nodes').warn(
{
node: type,