@data-client/endpoint
Version:
Declarative Network Interface Definitions
120 lines (108 loc) • 3.54 kB
text/typescript
import { isImmutable } from './ImmutableUtils.js';
import { Visit } from '../interface.js';
export default class PolymorphicSchema {
declare private _schemaAttribute: any;
protected schema: any;
constructor(
definition: any,
schemaAttribute?: string | ((...args: any) => any),
) {
if (schemaAttribute) {
this._schemaAttribute =
typeof schemaAttribute === 'string' ?
(input: any) => input[schemaAttribute]
: schemaAttribute;
}
this.define(definition);
}
get isSingleSchema() {
return !this._schemaAttribute;
}
define(definition: any) {
// sending Union into another Polymorphic gets hoisted
if ('_schemaAttribute' in definition && !this._schemaAttribute) {
this.schema = definition.schema;
this._schemaAttribute = definition._schemaAttribute;
} else {
this.schema = definition;
}
}
getSchemaAttribute(input: any, parent: any, key: any) {
return !this.isSingleSchema && this._schemaAttribute(input, parent, key);
}
inferSchema(input: any, parent: any, key: any) {
if (this.isSingleSchema) {
return this.schema;
}
const attr = this.getSchemaAttribute(input, parent, key);
return this.schema[attr];
}
schemaKey(): string {
if (this.isSingleSchema) {
return this.schema.key;
}
return Object.values(this.schema).join(';');
}
normalizeValue(value: any, parent: any, key: any, args: any[], visit: Visit) {
if (!value) return value;
const schema = this.inferSchema(value, parent, key);
if (!schema) {
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
const attr = this.getSchemaAttribute(value, parent, key);
console.warn(
`Schema attribute ${JSON.stringify(
attr,
undefined,
2,
)} is not expected.
Expected one of: ${Object.keys(this.schema)
.map(k => `"${k}"`)
.join(', ')}
Value: ${JSON.stringify(value, undefined, 2)}`,
);
}
return value;
}
const normalizedValue = visit(schema, value, parent, key, args);
return (
this.isSingleSchema ||
normalizedValue === undefined ||
normalizedValue === null
) ?
normalizedValue
: {
id: normalizedValue,
schema: this.getSchemaAttribute(value, parent, key),
};
}
// value is guaranteed by caller to not be null
denormalizeValue(value: any, unvisit: any) {
const schemaKey =
!this.isSingleSchema &&
value &&
(isImmutable(value) ? value.get('schema') : value.schema);
if (!this.isSingleSchema && !schemaKey) {
// denormalize should also handle 'passthrough' values (not normalized) and still
// construct the correct Entity instance
if (typeof value === 'object' && value !== null) {
const schema = this.inferSchema(value, undefined, undefined);
if (schema) return unvisit(schema, value);
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production' && value) {
console.warn(
`TypeError: Unable to infer schema for ${this.constructor.name}
Value: ${JSON.stringify(value, undefined, 2)}.`,
);
}
return value;
}
const id =
this.isSingleSchema ? undefined
: isImmutable(value) ? value.get('id')
: value.id;
const schema = this.isSingleSchema ? this.schema : this.schema[schemaKey];
return unvisit(schema, id || value);
}
}