import { glob } from 'glob'; import { JSONSchema4 } from 'json-schema'; import { dereference } from 'json-schema-ref-parser'; import { PgType } from '../abstractions'; /** * Returns true if a JSON schema property element is a primitive property. * @remarks * A primitive property is considered to be a property whose type is either * a primitive type or an array of primitive type. * @param schema - Schema element to analyze. */ export function isPrimitiveProperty(schema: JSONSchema4): boolean { if (schema.type === 'array') { return isPrimitiveType((schema.items as JSONSchema4)?.type); } return isPrimitiveType(schema.type); } /** * Returns true if a JSON schema property element is an object property. * @remarks * An object property is considered to be a property whose type is either * of type object or an array of type object. * @param schema - Schema element to analyze. */ export function isObjectProperty(schema: JSONSchema4): boolean { if (schema.type === 'array') { return (schema.items as JSONSchema4)?.type === 'object'; } return schema.type === 'object'; } /** * Returns true if schema property type is a primitive. * @param typeName - Type name to check. */ export function isPrimitiveType(typeName: unknown): boolean { return ( typeName === 'integer' || typeName === 'number' || typeName === 'string' || typeName === 'boolean' ); } /** * Returns a property (defined in 'properties') schema by name from JSON schema. * @param schema - JSON schema where the property exists. * @param propertyName - Name of the property to extract. */ export function getPropertySchema( schema: JSONSchema4, propertyName: string, ): JSONSchema4 { if (schema.properties !== undefined) { return schema.properties[propertyName]; } throw new Error(`No properties are defined in the schema.`); } /** * Maps JSON schema's property type to a Postgres type. * @param schema - Property schema. * @throws If the schema property type is not supported. */ export function mapToPgType(schema: JSONSchema4): PgType { const isArray = schema.type === 'array'; schema = isArray ? (schema?.items as JSONSchema4) : schema; if (schema.type === 'string') { return mapString(schema, isArray); } if (schema.type === 'integer') { return isArray ? 'INTEGER[]' : 'INTEGER'; } if (schema.type === 'boolean') { return 'BOOLEAN'; } throw new TypeError(`Unknown primitive type ${schema.type}`); } function mapString(schema: JSONSchema4, isArray: boolean): PgType { if (schema.format === 'date-time') { return 'TIMESTAMPTZ'; } if (isArray) { return 'TEXT[]'; } return 'TEXT'; } /** * Returns an array of all defined content metadata schemas (movie, collection etc.). * @param schema - Publish message schema. */ export async function getContentMetadataSchemas( schemaGlob: string, ): Promise { const files = glob.sync(schemaGlob); const schemas: JSONSchema4[] = []; for (const file of files) { const schema = (await dereference(file)) as JSONSchema4; schemas.push(schema); } return schemas; } /** * Separates primitive properties from object properties. * @param schema - Content metadata schema. * @param ignoredProperties - Array of ignored properties. * @returns Tuple of separated properties: first element will be primitive properties, second object properties. */ export function separateProperties( schema: JSONSchema4, ignoredProperties: string[], ): [{ [p: string]: JSONSchema4 }, { [p: string]: JSONSchema4 }] { const primitiveProperties: { [p: string]: JSONSchema4 } = {}; const objectProperties: { [p: string]: JSONSchema4 } = {}; // 1. Extract primitive properties and create a ContentEntityTable for (const key in schema.properties) { if (ignoredProperties.includes(key)) { continue; } const property = schema.properties[key] as JSONSchema4; if (isPrimitiveProperty(property)) { primitiveProperties[key] = property; } else if (isObjectProperty(property)) { objectProperties[key] = property; } else { throw new TypeError(`Unsupported property in asset schema: ${property}`); } } return [primitiveProperties, objectProperties]; } /** * Filters out properties by name from a dictionary of JSON schema properties. * @param schemas - Dictionary of schemas. * @param excluded - Array of excluded names. */ export function filterProperties( schemas: { [p: string]: JSONSchema4 }, excluded: string[], ): { [p: string]: JSONSchema4 } { const included = Object.keys(schemas).filter((k) => excluded.indexOf(k) < 0); return included.reduce((obj, key) => ({ ...obj, [key]: schemas[key] }), {}); } /** * Returns schema's title, throws if it is undefined. * @param schema - JSON schema */ export function getSchemaTitle(schema: JSONSchema4): string { if (schema.title) { return schema.title; } throw new Error('Schema title not set.'); }