diff --git a/shared/src/commands/generate/components/instantiate.ts b/shared/src/commands/generate/components/instantiate.ts index 551bda484..0d028fb21 100644 --- a/shared/src/commands/generate/components/instantiate.ts +++ b/shared/src/commands/generate/components/instantiate.ts @@ -1,10 +1,10 @@ import { Logger } from "winston"; import { initLogger } from "../../helper.js"; import { SchemaDirectory } from "../schema-directory.js"; -import { logRequiredMessage, mergeSchemas } from "../util.js"; +import { appendPath, logRequiredMessage, mergeSchemas, renderPath } from "../util.js"; import { getPropertyValue } from "./property.js"; -export function instantiateGenericObject(definition: object, schemaDirectory: SchemaDirectory, objectType: string, debug: boolean = false, instantiateAll: boolean = false): object { +export function instantiateGenericObject(definition: object, schemaDirectory: SchemaDirectory, objectType: string, path: string[], debug: boolean = false, instantiateAll: boolean = false): object { const logger = initLogger(debug); let fullDefinition = definition; if (definition['$ref']) { @@ -24,8 +24,11 @@ export function instantiateGenericObject(definition: object, schemaDirectory: Sc const out = {}; for (const [key, detail] of Object.entries(fullDefinition['properties'])) { + const currentPath = appendPath(path, key); + const renderedPath = renderPath(currentPath); + if (!instantiateAll && required && !required.includes(key)) { // TODO should we always do interfaces even if not required? - logger.debug('Skipping property ' + key + ' as it is not marked as required.'); + logger.debug(`${renderedPath}: Skipping property ${key} as it is not marked as required.`); continue; } if (!!detail?.const) { @@ -33,15 +36,15 @@ export function instantiateGenericObject(definition: object, schemaDirectory: Sc } if (detail?.type === 'object') { // recursive instantiation - logger.info('Recursively instantiating an ' + objectType + ' object'); - out[key] = instantiateGenericObject(detail, schemaDirectory, objectType, instantiateAll, debug); + logger.info(`${renderedPath}: Recursively instantiating an ${objectType} object`); + out[key] = instantiateGenericObject(detail, schemaDirectory, objectType, currentPath, instantiateAll, debug); } - else if (detail?.type === 'array' && isArrayObjectComplex(detail, logger)) { - logger.info('Recursively instantiating an array object.'); + else if (detail?.type === 'array' && isArrayObjectComplex(detail, logger, renderedPath)) { + logger.info(`${renderedPath}: Recursively instantiating an array object.`); // isArrayObjectComplex ensures this is present const prefixItems = detail.prefixItems; - out[key] = instantiateArray(prefixItems, schemaDirectory, objectType, instantiateAll, debug); + out[key] = instantiateArray(prefixItems, schemaDirectory, objectType, currentPath, instantiateAll, debug); } else { out[key] = getPropertyValue(key, detail); @@ -50,22 +53,19 @@ export function instantiateGenericObject(definition: object, schemaDirectory: Sc return out; } -function isArrayObjectComplex(detail: any, logger: Logger) { - console.log(detail) +function isArrayObjectComplex(detail: any, logger: Logger, pathContext: string) { if (!detail) { return false; } - console.log(detail) - const arrayContentsType = detail.items?.type if (!!arrayContentsType && ['integer', 'number', 'boolean', 'string', 'const'].includes(arrayContentsType)) { - logger.info('Skipping recursive instantiation of array as it has a simple type and no prefixItems') + logger.info(`${pathContext}: Skipping recursive instantiation of array as it has a simple type and no prefixItems`) return false } if (!!detail.prefixItems && !!detail.items) { - logger.warn("Both 'items' and 'prefixItems' are defined on this array schema; only prefixItems will be instantiated.") + logger.warn(`${pathContext}: Both 'items' and 'prefixItems' are defined on this array schema; only prefixItems will be instantiated.`) } if (!!detail.prefixItems) { @@ -77,57 +77,15 @@ function isArrayObjectComplex(detail: any, logger: Logger) { return false; } -export function instantiateArray(prefixItems: object[], schemaDirectory: SchemaDirectory, objectType: string, instantiateAll: boolean, debug: boolean) { +export function instantiateArray(prefixItems: object[], schemaDirectory: SchemaDirectory, objectType: string, path: string[], instantiateAll: boolean, debug: boolean) { const logger = initLogger(debug); const output = []; - logger.debug("Instantiating elements of array as defined in prefixItems") - for (const item of prefixItems) { - output.push(instantiateGenericObject(item, schemaDirectory, objectType, debug, instantiateAll)); + logger.debug(`${path}: Instantiating elements of array as defined in prefixItems`) + for (const [index, element] of prefixItems.entries()) { + const currentPath = appendPath(path, index) + output.push(instantiateGenericObject(element, schemaDirectory, objectType, currentPath, debug, instantiateAll)); } return output; -} - -export function instantiateArrayObject(definition: object, schemaDirectory: SchemaDirectory, objectType: string, debug: boolean = false, instantiateAll: boolean = false): object { - const logger = initLogger(debug); - let fullDefinition = definition; - if (definition['$ref']) { - const ref = definition['$ref']; - const schemaDef = schemaDirectory.getDefinition(ref); - - fullDefinition = mergeSchemas(schemaDef, definition); - } - logger.debug('Generating ' + objectType + ' object from ' + JSON.stringify(fullDefinition)); - - if (!('properties' in fullDefinition)) { - return {}; - } - - const required = fullDefinition['required']; - logRequiredMessage(logger, required, instantiateAll); - - const out = {}; - for (const [key, detail] of Object.entries(fullDefinition['properties'])) { - if (!instantiateAll && required && !required.includes(key)) { - logger.debug('Skipping property ' + key + ' as it is not marked as required.'); - continue; - } - if (detail?.type === 'object') { - // recursive instantiation - logger.debug('Recursively instantiating an ' + objectType + ' object'); - out[key] = instantiateGenericObject(detail, schemaDirectory, objectType, instantiateAll, debug); - } - else if (detail?.type === 'array') { - if (key === 'interfaces') - logger.debug('Instantiating interfaces for a node object.'); - else - logger.debug('Recursively instantiating an array object.'); - - } - else { - out[key] = getPropertyValue(key, detail); - } - } - return out; } \ No newline at end of file diff --git a/shared/src/commands/generate/components/metadata.spec.ts b/shared/src/commands/generate/components/metadata.spec.ts index e5bf726a2..cf079c03a 100644 --- a/shared/src/commands/generate/components/metadata.spec.ts +++ b/shared/src/commands/generate/components/metadata.spec.ts @@ -40,7 +40,7 @@ describe('instantiateMetadataObject', () => { } } }; - expect(instantiateMetadataObject(metadataDef, mockSchemaDir, false, true)) + expect(instantiateMetadataObject(metadataDef, mockSchemaDir, [], false, true)) .toEqual( { 'string-prop': '{{ STRING_PROP }}', @@ -65,7 +65,7 @@ describe('instantiateMetadataObject', () => { } } }; - expect(instantiateMetadataObject(metadataDef, mockSchemaDir, false, true)) + expect(instantiateMetadataObject(metadataDef, mockSchemaDir, [], false, true)) .toEqual( { 'property-name': { @@ -99,7 +99,7 @@ describe('instantiateMetadataObject', () => { spy.mockReturnValue(returnedDef); - expect(instantiateMetadataObject(metadataDef, mockSchemaDir, false, true)) + expect(instantiateMetadataObject(metadataDef, mockSchemaDir, [], false, true)) .toEqual( { 'property-name': { diff --git a/shared/src/commands/generate/components/metadata.ts b/shared/src/commands/generate/components/metadata.ts index cfabf83e6..aa369f746 100644 --- a/shared/src/commands/generate/components/metadata.ts +++ b/shared/src/commands/generate/components/metadata.ts @@ -1,11 +1,11 @@ import { initLogger } from '../../helper.js'; import { SchemaDirectory } from '../schema-directory.js'; -import { logRequiredMessage, mergeSchemas } from '../util.js'; +import { appendPath, logRequiredMessage, mergeSchemas } from '../util.js'; import { instantiateGenericObject } from './instantiate.js'; import { getPropertyValue } from './property.js'; -export function instantiateMetadataObject(definition: object, schemaDirectory: SchemaDirectory, debug: boolean = false, instantiateAll: boolean = false): object { - return instantiateGenericObject(definition, schemaDirectory, 'metadata', debug, instantiateAll); +export function instantiateMetadataObject(definition: object, schemaDirectory: SchemaDirectory, path: string[], debug: boolean = false, instantiateAll: boolean = false): object { + return instantiateGenericObject(definition, schemaDirectory, 'metadata', path, debug, instantiateAll); } export function instantiateAllMetadata(pattern: object, schemaDirectory: SchemaDirectory, debug: boolean = false, instantiateAll: boolean = false): object[] { @@ -20,8 +20,9 @@ export function instantiateAllMetadata(pattern: object, schemaDirectory: SchemaD } const outputMetadata = []; - for (const node of metadataObjects) { - outputMetadata.push(instantiateMetadataObject(node, schemaDirectory, debug, instantiateAll)); + for (const [index, metadataObj] of metadataObjects.entries()) { + const path = appendPath(['metadata'], index); + outputMetadata.push(instantiateMetadataObject(metadataObj, schemaDirectory, path, debug, instantiateAll)); } return outputMetadata; } \ No newline at end of file diff --git a/shared/src/commands/generate/components/node.ts b/shared/src/commands/generate/components/node.ts index f49beece4..afe5b83ff 100644 --- a/shared/src/commands/generate/components/node.ts +++ b/shared/src/commands/generate/components/node.ts @@ -2,7 +2,7 @@ import { initLogger } from '../../helper.js'; import { SchemaDirectory } from '../schema-directory.js'; -import { logRequiredMessage, mergeSchemas } from '../util.js'; +import { appendPath, logRequiredMessage, mergeSchemas } from '../util.js'; import { instantiateGenericObject } from './instantiate.js'; import { getPropertyValue } from './property.js'; @@ -13,8 +13,8 @@ import { getPropertyValue } from './property.js'; * @param debug Whether to log debug detail. * @returns An instantiated node. */ -export function instantiateNode(nodeDef: any, schemaDirectory: SchemaDirectory, debug: boolean = false, instantiateAll: boolean = false): any { - return instantiateGenericObject(nodeDef, schemaDirectory, 'node', debug, instantiateAll); +export function instantiateNode(nodeDef: any, schemaDirectory: SchemaDirectory, path: string[], debug: boolean = false, instantiateAll: boolean = false): any { + return instantiateGenericObject(nodeDef, schemaDirectory, 'node', path, debug, instantiateAll); } /** @@ -36,8 +36,9 @@ export function instantiateNodes(pattern: any, schemaDirectory: SchemaDirectory, } const outputNodes = []; - for (const node of nodes) { - outputNodes.push(instantiateNode(node, schemaDirectory, debug, instantiateAll)); + for (const [index, node] of nodes.entries()) { + const path = appendPath(['nodes'], index) + outputNodes.push(instantiateNode(node, schemaDirectory, path, debug, instantiateAll)); } return outputNodes; } diff --git a/shared/src/commands/generate/components/relationship.ts b/shared/src/commands/generate/components/relationship.ts index 48288beb8..321f47203 100644 --- a/shared/src/commands/generate/components/relationship.ts +++ b/shared/src/commands/generate/components/relationship.ts @@ -2,7 +2,7 @@ import { initLogger } from '../../helper.js'; import { SchemaDirectory } from '../schema-directory.js'; -import { logRequiredMessage, mergeSchemas } from '../util.js'; +import { appendPath, logRequiredMessage, mergeSchemas } from '../util.js'; import { instantiateGenericObject } from './instantiate.js'; import { getPropertyValue } from './property.js'; @@ -11,11 +11,12 @@ import { getPropertyValue } from './property.js'; * Instantiates an individual relationship. * @param relationshipDef The relationship definition to instantiate * @param schemaDirectory The schema directory to resolve refs against. + * @param path The current path in the document, for logging purposes. * @param debug Whether to log debug detail * @returns An instantiated relationship. */ -function instantiateRelationship(relationshipDef: object, schemaDirectory: SchemaDirectory, debug: boolean = false, instantiateAll: boolean = false): object { - return instantiateGenericObject(relationshipDef, schemaDirectory, 'relationship', debug, instantiateAll); +function instantiateRelationship(relationshipDef: object, schemaDirectory: SchemaDirectory, path: string[], debug: boolean = false, instantiateAll: boolean = false): object { + return instantiateGenericObject(relationshipDef, schemaDirectory, 'relationship', path, debug, instantiateAll); } /** @@ -38,8 +39,9 @@ export function instantiateRelationships(pattern: any, schemaDirectory: SchemaDi } const outputRelationships = []; - for (const relationship of relationships) { - outputRelationships.push(instantiateRelationship(relationship, schemaDirectory, debug, instantiateAll)); + for (const [index, relationship] of relationships.entries()) { + const path = appendPath(['relationships'], index); + outputRelationships.push(instantiateRelationship(relationship, schemaDirectory, path, debug, instantiateAll)); } return outputRelationships; diff --git a/shared/src/commands/generate/util.ts b/shared/src/commands/generate/util.ts index 8dc57ddf9..9eea55286 100644 --- a/shared/src/commands/generate/util.ts +++ b/shared/src/commands/generate/util.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import { Logger } from 'winston'; +import pointer from 'json-pointer'; /** * Recursively merge two schemas into a new object, without modifying either. @@ -24,4 +25,12 @@ export function logRequiredMessage(logger: Logger, required: string[], instantia } else { logger.debug('Required properties: ' + required); } +} + +export function appendPath(path: string[], element) : string[] { + return [...path, element]; +} + +export function renderPath(path: string[]): string { + return pointer.compile(path); } \ No newline at end of file