Skip to content

Commit

Permalink
fix(schema): adds support of selecting schema without discriminator f…
Browse files Browse the repository at this point in the history
…ields in discriminatedSchema (#184)
  • Loading branch information
asadali214 authored Jul 18, 2024
1 parent 42e868d commit 74b5483
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 83 deletions.
85 changes: 56 additions & 29 deletions packages/schema/src/types/discriminatedObject.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Schema, SchemaMappedType, SchemaType } from '../schema';
import {
Schema,
SchemaContextCreator,
SchemaMappedType,
SchemaType,
SchemaValidationError,
} from '../schema';
import { objectEntries } from '../utils';
import { ObjectXmlOptions } from './object';

Expand All @@ -14,10 +20,10 @@ export function discriminatedObject<
defaultDiscriminator: keyof TDiscrimMap,
xmlOptions?: ObjectXmlOptions
): Schema<any, any> {
const schemaSelector = (
const selectSchemaWithDisc = (
value: unknown,
discriminatorProp: string | TDiscrimProp | TDiscrimMappedProp,
isAttr: boolean = false
isAttr?: boolean
) => {
if (
typeof value === 'object' &&
Expand All @@ -39,43 +45,64 @@ export function discriminatedObject<
return discriminatorMap[discriminatorValue];
}
}
return undefined;
};
const allSchemas = Object.values(discriminatorMap).reverse();
const selectSchema = (
value: unknown,
discriminatorProp: string | TDiscrimProp | TDiscrimMappedProp,
validater: (schema: TSchema) => SchemaValidationError[],
isAttr?: boolean
) => {
const schema = selectSchemaWithDisc(value, discriminatorProp, isAttr);
if (typeof schema !== 'undefined') {
return schema;
}
// Try checking with discriminator matching
for (const key in allSchemas) {
if (validater(allSchemas[key]).length === 0) {
return allSchemas[key];
}
}
// Fallback to default schema
return discriminatorMap[defaultDiscriminator];
};

const mapJsonSchema = (value: unknown, ctxt: SchemaContextCreator) =>
selectSchema(value, discriminatorPropName, (schema) =>
schema.validateBeforeMap(value, ctxt)
);

const mapXmlSchema = (value: unknown, ctxt: SchemaContextCreator) =>
selectSchema(
value,
xmlOptions?.xmlName ?? discriminatorPropName,
(schema) => schema.validateBeforeMapXml(value, ctxt),
xmlOptions?.isAttr
);

const unmapSchema = (value: unknown, ctxt: SchemaContextCreator) =>
selectSchema(value, discriminatorMappedPropName, (schema) =>
schema.validateBeforeUnmap(value, ctxt)
);

return {
type: () =>
`DiscriminatedUnion<${discriminatorPropName},[${objectEntries(
`DiscriminatedUnion<${discriminatorPropName as string},[${objectEntries(
discriminatorMap
)
.map(([_, v]) => v.type)
.join(',')}]>`,
map: (value, ctxt) =>
schemaSelector(value, discriminatorPropName).map(value, ctxt),
unmap: (value, ctxt) =>
schemaSelector(value, discriminatorMappedPropName).unmap(value, ctxt),
map: (value, ctxt) => mapJsonSchema(value, ctxt).map(value, ctxt),
unmap: (value, ctxt) => unmapSchema(value, ctxt).unmap(value, ctxt),
validateBeforeMap: (value, ctxt) =>
schemaSelector(value, discriminatorPropName).validateBeforeMap(
value,
ctxt
),
mapJsonSchema(value, ctxt).validateBeforeMap(value, ctxt),
validateBeforeUnmap: (value, ctxt) =>
schemaSelector(value, discriminatorMappedPropName).validateBeforeUnmap(
value,
ctxt
),
mapXml: (value, ctxt) =>
schemaSelector(
value,
xmlOptions?.xmlName ?? discriminatorPropName,
xmlOptions?.isAttr
).mapXml(value, ctxt),
unmapXml: (value, ctxt) =>
schemaSelector(value, discriminatorMappedPropName).unmapXml(value, ctxt),
unmapSchema(value, ctxt).validateBeforeUnmap(value, ctxt),
mapXml: (value, ctxt) => mapXmlSchema(value, ctxt).mapXml(value, ctxt),
unmapXml: (value, ctxt) => unmapSchema(value, ctxt).unmapXml(value, ctxt),
validateBeforeMapXml: (value, ctxt) =>
schemaSelector(
value,
xmlOptions?.xmlName ?? discriminatorPropName,
xmlOptions?.isAttr
).validateBeforeMapXml(value, ctxt),
mapXmlSchema(value, ctxt).validateBeforeMapXml(value, ctxt),
};
}

Expand Down
3 changes: 2 additions & 1 deletion packages/schema/src/types/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { OptionalizeObject } from '../typeUtils';
import {
isOptional,
isOptionalNullable,
isOptionalOrNullableType,
literalToString,
objectEntries,
objectKeyEncode,
Expand Down Expand Up @@ -377,7 +378,7 @@ function validateValueObject({
ctxt.createChild(propTypePrefix + key, valueObject[key], schema)
)
);
} else if (schema.type().indexOf('Optional<') !== 0) {
} else if (!isOptionalOrNullableType(schema.type())) {
// Add to missing keys if it is not an optional property
missingProps.add(key);
}
Expand Down
13 changes: 10 additions & 3 deletions packages/schema/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,16 @@ export function isOptional(type: string, value: unknown): boolean {
}

export function isOptionalNullable(type: string, value: unknown): boolean {
return isOptionalAndNullableType(type) && isNullOrMissing(value);
}

export function isOptionalAndNullableType(type: string): boolean {
return (
(type.startsWith('Optional<Nullable<') ||
type.startsWith('Nullable<Optional<')) &&
isNullOrMissing(value)
type.startsWith('Optional<Nullable<') ||
type.startsWith('Nullable<Optional<')
);
}

export function isOptionalOrNullableType(type: string): boolean {
return type.startsWith('Optional<') || type.startsWith('Nullable<');
}
Loading

0 comments on commit 74b5483

Please sign in to comment.