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}})", "unsupportedAnyOfLength": "too many union members ({{count}})",
"unsupportedMismatchedUnion": "mismatched CollectionOrScalar type with base types {{firstType}} and {{secondType}}", "unsupportedMismatchedUnion": "mismatched CollectionOrScalar type with base types {{firstType}} and {{secondType}}",
"unableToParseFieldType": "unable to parse field type", "unableToParseFieldType": "unable to parse field type",
"unableToExtractEnumOptions": "unable to extract enum options",
"uNetField": "UNet", "uNetField": "UNet",
"uNetFieldDescription": "UNet submodel.", "uNetFieldDescription": "UNet submodel.",
"unhandledInputProperty": "Unhandled input property", "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. * Raised when a field cannot be parsed from a field schema.
*/ */
export class FieldTypeParseError extends Error { export class FieldParseError extends Error {
/** /**
* Create FieldTypeParseError * Create FieldTypeParseError
* @param {String} message * @param {String} message
@ -56,18 +56,3 @@ export class FieldTypeParseError extends Error {
this.name = this.constructor.name; 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, VAEModelFieldInputTemplate,
isStatefulFieldType, isStatefulFieldType,
} from 'features/nodes/types/field'; } 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 // eslint-disable-next-line @typescript-eslint/no-explicit-any
type FieldInputTemplateBuilder<T extends FieldInputTemplate = any> = // valid `any`! type FieldInputTemplateBuilder<T extends FieldInputTemplate = any> = // valid `any`!
@ -321,7 +326,28 @@ const buildImageFieldInputTemplate: FieldInputTemplateBuilder<
const buildEnumFieldInputTemplate: FieldInputTemplateBuilder< const buildEnumFieldInputTemplate: FieldInputTemplateBuilder<
EnumFieldInputTemplate EnumFieldInputTemplate
> = ({ schemaObject, baseField, isCollection, isCollectionOrScalar }) => { > = ({ 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 = { const template: EnumFieldInputTemplate = {
...baseField, ...baseField,
type: { type: {

View File

@ -1,10 +1,4 @@
import { t } from 'i18next'; import { FieldParseError } from 'features/nodes/types/error';
import { isArray } from 'lodash-es';
import { OpenAPIV3_1 } from 'openapi-types';
import {
FieldTypeParseError,
UnsupportedFieldTypeError,
} from 'features/nodes/types/error';
import { FieldType } from 'features/nodes/types/field'; import { FieldType } from 'features/nodes/types/field';
import { import {
OpenAPIV3_1SchemaOrRef, OpenAPIV3_1SchemaOrRef,
@ -14,6 +8,9 @@ import {
isRefObject, isRefObject,
isSchemaObject, isSchemaObject,
} from 'features/nodes/types/openapi'; } 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. * Transforms an invocation output ref object to field type.
@ -70,7 +67,7 @@ export const parseFieldType = (
// This is a single ref type // This is a single ref type
const name = refObjectToSchemaName(allOf[0]); const name = refObjectToSchemaName(allOf[0]);
if (!name) { if (!name) {
throw new FieldTypeParseError( throw new FieldParseError(
t('nodes.unableToExtractSchemaNameFromRef') t('nodes.unableToExtractSchemaNameFromRef')
); );
} }
@ -95,7 +92,7 @@ export const parseFieldType = (
if (isRefObject(filteredAnyOf[0])) { if (isRefObject(filteredAnyOf[0])) {
const name = refObjectToSchemaName(filteredAnyOf[0]); const name = refObjectToSchemaName(filteredAnyOf[0]);
if (!name) { if (!name) {
throw new FieldTypeParseError( throw new FieldParseError(
t('nodes.unableToExtractSchemaNameFromRef') t('nodes.unableToExtractSchemaNameFromRef')
); );
} }
@ -120,7 +117,7 @@ export const parseFieldType = (
if (filteredAnyOf.length !== 2) { if (filteredAnyOf.length !== 2) {
// This is a union of more than 2 types, which we don't support // This is a union of more than 2 types, which we don't support
throw new UnsupportedFieldTypeError( throw new FieldParseError(
t('nodes.unsupportedAnyOfLength', { t('nodes.unsupportedAnyOfLength', {
count: filteredAnyOf.length, count: filteredAnyOf.length,
}) })
@ -167,7 +164,7 @@ export const parseFieldType = (
}; };
} }
throw new UnsupportedFieldTypeError( throw new FieldParseError(
t('nodes.unsupportedMismatchedUnion', { t('nodes.unsupportedMismatchedUnion', {
firstType, firstType,
secondType, secondType,
@ -186,7 +183,7 @@ export const parseFieldType = (
if (isSchemaObject(schemaObject.items)) { if (isSchemaObject(schemaObject.items)) {
const itemType = schemaObject.items.type; const itemType = schemaObject.items.type;
if (!itemType || isArray(itemType)) { if (!itemType || isArray(itemType)) {
throw new UnsupportedFieldTypeError( throw new FieldParseError(
t('nodes.unsupportedArrayItemType', { t('nodes.unsupportedArrayItemType', {
type: itemType, type: itemType,
}) })
@ -196,7 +193,7 @@ export const parseFieldType = (
const name = OPENAPI_TO_FIELD_TYPE_MAP[itemType]; const name = OPENAPI_TO_FIELD_TYPE_MAP[itemType];
if (!name) { if (!name) {
// it's 'null', 'object', or 'array' - skip // it's 'null', 'object', or 'array' - skip
throw new UnsupportedFieldTypeError( throw new FieldParseError(
t('nodes.unsupportedArrayItemType', { t('nodes.unsupportedArrayItemType', {
type: itemType, type: itemType,
}) })
@ -212,7 +209,7 @@ export const parseFieldType = (
// This is a ref object, extract the type name // This is a ref object, extract the type name
const name = refObjectToSchemaName(schemaObject.items); const name = refObjectToSchemaName(schemaObject.items);
if (!name) { if (!name) {
throw new FieldTypeParseError( throw new FieldParseError(
t('nodes.unableToExtractSchemaNameFromRef') t('nodes.unableToExtractSchemaNameFromRef')
); );
} }
@ -226,7 +223,7 @@ export const parseFieldType = (
const name = OPENAPI_TO_FIELD_TYPE_MAP[schemaObject.type]; const name = OPENAPI_TO_FIELD_TYPE_MAP[schemaObject.type];
if (!name) { if (!name) {
// it's 'null', 'object', or 'array' - skip // it's 'null', 'object', or 'array' - skip
throw new UnsupportedFieldTypeError( throw new FieldParseError(
t('nodes.unsupportedArrayItemType', { t('nodes.unsupportedArrayItemType', {
type: schemaObject.type, type: schemaObject.type,
}) })
@ -242,9 +239,7 @@ export const parseFieldType = (
} else if (isRefObject(schemaObject)) { } else if (isRefObject(schemaObject)) {
const name = refObjectToSchemaName(schemaObject); const name = refObjectToSchemaName(schemaObject);
if (!name) { if (!name) {
throw new FieldTypeParseError( throw new FieldParseError(t('nodes.unableToExtractSchemaNameFromRef'));
t('nodes.unableToExtractSchemaNameFromRef')
);
} }
return { return {
name, name,
@ -252,5 +247,5 @@ export const parseFieldType = (
isCollectionOrScalar: false, 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 { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize'; import { parseify } from 'common/util/serialize';
import { t } from 'i18next'; import { FieldParseError } from 'features/nodes/types/error';
import { reduce } from 'lodash-es';
import { OpenAPIV3_1 } from 'openapi-types';
import {
FieldTypeParseError,
UnsupportedFieldTypeError,
} from 'features/nodes/types/error';
import { import {
FieldInputTemplate, FieldInputTemplate,
FieldOutputTemplate, FieldOutputTemplate,
@ -18,6 +12,9 @@ import {
isInvocationOutputSchemaObject, isInvocationOutputSchemaObject,
isInvocationSchemaObject, isInvocationSchemaObject,
} from 'features/nodes/types/openapi'; } 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 { buildFieldInputTemplate } from './buildFieldInputTemplate';
import { buildFieldOutputTemplate } from './buildFieldOutputTemplate'; import { buildFieldOutputTemplate } from './buildFieldOutputTemplate';
import { parseFieldType } from './parseFieldType'; import { parseFieldType } from './parseFieldType';
@ -133,10 +130,7 @@ export const parseSchema = (
inputsAccumulator[propertyName] = fieldInputTemplate; inputsAccumulator[propertyName] = fieldInputTemplate;
} catch (e) { } catch (e) {
if ( if (e instanceof FieldParseError) {
e instanceof FieldTypeParseError ||
e instanceof UnsupportedFieldTypeError
) {
logger('nodes').warn( logger('nodes').warn(
{ {
node: type, node: type,
@ -225,10 +219,7 @@ export const parseSchema = (
outputsAccumulator[propertyName] = fieldOutputTemplate; outputsAccumulator[propertyName] = fieldOutputTemplate;
} catch (e) { } catch (e) {
if ( if (e instanceof FieldParseError) {
e instanceof FieldTypeParseError ||
e instanceof UnsupportedFieldTypeError
) {
logger('nodes').warn( logger('nodes').warn(
{ {
node: type, node: type,