dynamodb-toolbox
Version:
Lightweight and type-safe query builder for DynamoDB and TypeScript.
119 lines (118 loc) • 4.44 kB
JavaScript
import { DynamoDBToolboxError } from '../../../errors/index.js';
import { formatArrayPath } from '../../../schema/actions/utils/formatArrayPath.js';
import { cloneDeep } from '../../../utils/cloneDeep.js';
import { isFunction } from '../../../utils/validation/isFunction.js';
import { anySchemaParser } from './any.js';
import { anyOfSchemaParser } from './anyOf.js';
import { listSchemaParser } from './list.js';
import { mapSchemaParser } from './map.js';
import { primitiveSchemaParser } from './primitive.js';
import { recordSchemaParser } from './record.js';
import { setSchemaParser } from './set.js';
import { defaultParseExtension, isRequired } from './utils.js';
export function* schemaParser(schema, inputValue, options = {}) {
const { mode = 'put', fill = true, transform = true, defined = false,
/**
* @debt type "Maybe there's a way not to have to cast here"
*/
parseExtension = defaultParseExtension, valuePath } = options;
let filledValue = inputValue;
let nextFill = fill;
if (nextFill && filledValue === undefined) {
let defaultedValue = undefined;
const modeDefault = getDefaulter(schema, mode);
defaultedValue = isFunction(modeDefault) ? modeDefault() : cloneDeep(modeDefault);
const itemInput = yield defaultedValue;
let linkedValue = defaultedValue;
if (linkedValue === undefined && itemInput !== undefined) {
const modeLink = getModeLink(schema, mode);
linkedValue = (isFunction(modeLink) ? modeLink(itemInput) : linkedValue);
}
yield linkedValue;
filledValue = linkedValue;
nextFill = false;
}
const nextOpts = { ...options, fill: nextFill };
const { isExtension, extensionParser, unextendedInput } = parseExtension(schema, filledValue, {
transform,
valuePath
});
if (isExtension) {
if (nextFill) {
// parseExtension does not fill values
// If fill was set to `true` and input was defined, we yield it twice for fill steps
const defaultedValue = filledValue;
yield defaultedValue;
const linkedValue = defaultedValue;
yield linkedValue;
}
return yield* extensionParser();
}
if (unextendedInput === undefined) {
const path = valuePath !== undefined ? formatArrayPath(valuePath) : undefined;
// We don't need to fill
if (isRequired(schema, mode) || defined) {
throw new DynamoDBToolboxError('parsing.attributeRequired', {
message: `Attribute${path !== undefined ? ` '${path}'` : ''} is required.`,
path
});
}
const parsedValue = unextendedInput;
if (transform) {
yield parsedValue;
}
else {
return parsedValue;
}
const transformedValue = parsedValue;
return transformedValue;
}
switch (schema.type) {
case 'any':
return yield* anySchemaParser(schema, unextendedInput, nextOpts);
case 'null':
case 'boolean':
case 'number':
case 'string':
case 'binary':
return yield* primitiveSchemaParser(schema, unextendedInput, nextOpts);
case 'set':
return yield* setSchemaParser(schema, unextendedInput, nextOpts);
case 'list':
return yield* listSchemaParser(schema, unextendedInput, nextOpts);
case 'map':
return yield* mapSchemaParser(schema, unextendedInput, nextOpts);
case 'record':
return yield* recordSchemaParser(schema, unextendedInput, nextOpts);
case 'anyOf':
return yield* anyOfSchemaParser(schema, unextendedInput, nextOpts);
}
}
const getDefaulter = (schema, mode) => {
const { props } = schema;
if (props.key) {
return props.keyDefault;
}
switch (mode) {
case 'key':
return props.keyDefault;
case 'put':
return props.putDefault;
case 'update':
return props.updateDefault;
}
};
const getModeLink = (schema, mode) => {
const { props } = schema;
if (props.key) {
return props.keyLink;
}
switch (mode) {
case 'key':
return props.keyLink;
case 'put':
return props.putLink;
case 'update':
return props.updateLink;
}
};