diff --git a/packages/core/src/http/requestBuilder.ts b/packages/core/src/http/requestBuilder.ts index 358b8f5..316897c 100644 --- a/packages/core/src/http/requestBuilder.ts +++ b/packages/core/src/http/requestBuilder.ts @@ -673,15 +673,14 @@ function mergePath(left: string, right?: string): string { if (!right || right === '') { return left; } + // remove all occurances of `/` (if any) from the end of left path + left = left.replace('/', ' ').trimEnd().replace(' ', '/'); + // remove all occurances of `/` (if any) from the start of right sub-path + right = right.replace('/', ' ').trimStart().replace(' ', '/'); - if (left[left.length - 1] === '/' && right[0] === '/') { - return left + right.substr(1); - } else if (left[left.length - 1] === '/' || right[0] === '/') { - return left + right; - } else { - return `${left}/${right}`; - } + return `${left}/${right}`; } + function parseJsonResult(schema: Schema, res: ApiResponse): T { if (typeof res.body !== 'string') { throw new Error( @@ -707,6 +706,7 @@ function parseJsonResult(schema: Schema, res: ApiResponse): T { new ResponseValidationError(res, errors); return validateJson(schema, parsed, (errors) => resInvalidErr(errors)); } + function validateJson( schema: Schema, value: any, diff --git a/packages/schema/src/types/discriminatedObject.ts b/packages/schema/src/types/discriminatedObject.ts index 71f1a12..df02987 100644 --- a/packages/schema/src/types/discriminatedObject.ts +++ b/packages/schema/src/types/discriminatedObject.ts @@ -1,5 +1,6 @@ import { Schema, + SchemaContextCreator, SchemaMappedType, SchemaType, SchemaValidationError, @@ -19,12 +20,10 @@ export function discriminatedObject< defaultDiscriminator: keyof TDiscrimMap, xmlOptions?: ObjectXmlOptions ): Schema { - const allSchemas = Object.values(discriminatorMap).reverse(); - const selectSchema = ( + const selectSchemaWithDisc = ( value: unknown, discriminatorProp: string | TDiscrimProp | TDiscrimMappedProp, - checker: (schema: TSchema) => SchemaValidationError[], - isAttr: boolean = false + isAttr?: boolean ) => { if ( typeof value === 'object' && @@ -46,14 +45,47 @@ 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 (checker(allSchemas[key]).length === 0) { + 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 as string},[${objectEntries( @@ -61,40 +93,16 @@ export function discriminatedObject< ) .map(([_, v]) => v.type) .join(',')}]>`, - map: (value, ctxt) => - selectSchema(value, discriminatorPropName, (schema) => - schema.validateBeforeMap(value, ctxt) - ).map(value, ctxt), - unmap: (value, ctxt) => - selectSchema(value, discriminatorMappedPropName, (schema) => - schema.validateBeforeUnmap(value, ctxt) - ).unmap(value, ctxt), + map: (value, ctxt) => mapJsonSchema(value, ctxt).map(value, ctxt), + unmap: (value, ctxt) => unmapSchema(value, ctxt).unmap(value, ctxt), validateBeforeMap: (value, ctxt) => - selectSchema(value, discriminatorPropName, (schema) => - schema.validateBeforeMap(value, ctxt) - ).validateBeforeMap(value, ctxt), + mapJsonSchema(value, ctxt).validateBeforeMap(value, ctxt), validateBeforeUnmap: (value, ctxt) => - selectSchema(value, discriminatorMappedPropName, (schema) => - schema.validateBeforeUnmap(value, ctxt) - ).validateBeforeUnmap(value, ctxt), - mapXml: (value, ctxt) => - selectSchema( - value, - xmlOptions?.xmlName ?? discriminatorPropName, - (schema) => schema.validateBeforeMapXml(value, ctxt), - xmlOptions?.isAttr - ).mapXml(value, ctxt), - unmapXml: (value, ctxt) => - selectSchema(value, discriminatorMappedPropName, (schema) => - schema.validateBeforeUnmap(value, ctxt) - ).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) => - selectSchema( - value, - xmlOptions?.xmlName ?? discriminatorPropName, - (schema) => schema.validateBeforeMapXml(value, ctxt), - xmlOptions?.isAttr - ).validateBeforeMapXml(value, ctxt), + mapXmlSchema(value, ctxt).validateBeforeMapXml(value, ctxt), }; } diff --git a/packages/schema/src/types/object.ts b/packages/schema/src/types/object.ts index 3317d4c..77e9fd9 100644 --- a/packages/schema/src/types/object.ts +++ b/packages/schema/src/types/object.ts @@ -9,6 +9,7 @@ import { OptionalizeObject } from '../typeUtils'; import { isOptional, isOptionalNullable, + isOptionalOrNullableType, literalToString, objectEntries, objectKeyEncode, @@ -377,10 +378,7 @@ function validateValueObject({ ctxt.createChild(propTypePrefix + key, valueObject[key], schema) ) ); - } else if ( - !schema.type().startsWith('Optional<') && - !schema.type().startsWith('Nullable<') - ) { + } else if (!isOptionalOrNullableType(schema.type())) { // Add to missing keys if it is not an optional property missingProps.add(key); } diff --git a/packages/schema/src/utils.ts b/packages/schema/src/utils.ts index 48ece39..f813f34 100644 --- a/packages/schema/src/utils.ts +++ b/packages/schema/src/utils.ts @@ -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