From ec17909433ab111e81bcf6e71613dadb664bd157 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Fri, 21 Mar 2025 19:17:27 +0200 Subject: [PATCH 1/7] add object types RE --- reverse_engineering/constants/types.js | 14 +++ .../helpers/getDefinitionCategoryByNameMap.js | 38 +++++++ reverse_engineering/helpers/sortByName.js | 6 +- reverse_engineering/mappers/field.js | 101 ++++++++++++++++++ reverse_engineering/mappers/schema.js | 7 +- .../mappers/typeDefinitions/objectType.js | 58 ++++++++++ .../typeDefinitions/typeDefinitions.js | 59 +++++++--- shared/types/fe.d.ts | 59 +--------- shared/types/re.d.ts | 49 ++++++++- shared/types/shared.d.ts | 57 ++++++++++ 10 files changed, 374 insertions(+), 74 deletions(-) create mode 100644 reverse_engineering/constants/types.js create mode 100644 reverse_engineering/helpers/getDefinitionCategoryByNameMap.js create mode 100644 reverse_engineering/mappers/field.js create mode 100644 reverse_engineering/mappers/typeDefinitions/objectType.js diff --git a/reverse_engineering/constants/types.js b/reverse_engineering/constants/types.js new file mode 100644 index 0000000..3adae48 --- /dev/null +++ b/reverse_engineering/constants/types.js @@ -0,0 +1,14 @@ +// graphql types +const BUILT_IN_SCALAR = { + String: 'String', + Int: 'Int', + Float: 'Float', + Boolean: 'Boolean', + ID: 'ID', +}; + +const BUILT_IN_SCALAR_LIST = Object.values(BUILT_IN_SCALAR); + +module.exports = { + BUILT_IN_SCALAR_LIST, +}; diff --git a/reverse_engineering/helpers/getDefinitionCategoryByNameMap.js b/reverse_engineering/helpers/getDefinitionCategoryByNameMap.js new file mode 100644 index 0000000..021fbae --- /dev/null +++ b/reverse_engineering/helpers/getDefinitionCategoryByNameMap.js @@ -0,0 +1,38 @@ +/** + * @import {DefinitionNode} from "graphql" + * @import {DefinitionNameToTypeNameMap} from "../../shared/types/re" + */ + +const { astNodeKind } = require('../constants/graphqlAST'); + +const kindToDefinitionTypeName = { + [astNodeKind.SCALAR_TYPE_DEFINITION]: 'Scalars', + [astNodeKind.ENUM_TYPE_DEFINITION]: 'Enums', + [astNodeKind.OBJECT_TYPE_DEFINITION]: 'Objects', + [astNodeKind.INTERFACE_TYPE_DEFINITION]: 'Interfaces', + [astNodeKind.UNION_TYPE_DEFINITION]: 'Unions', + [astNodeKind.INPUT_OBJECT_TYPE_DEFINITION]: 'Input objects', + [astNodeKind.DIRECTIVE_DEFINITION]: 'Directives', +}; + +/** + * Find nodes by kind with proper typing + * + * @param {object} options + * @param {DefinitionNode[]} options.nodes - The nodes to search + * @returns {DefinitionNameToTypeNameMap} The found nodes with proper type + */ +function getDefinitionCategoryByNameMap({ nodes }) { + return nodes + .filter(node => kindToDefinitionTypeName[node.kind]) + .reduce((acc, node) => { + if ('name' in node && node.name !== undefined) { + acc[node.name.value] = kindToDefinitionTypeName[node.kind]; + } + return acc; + }, {}); +} + +module.exports = { + getDefinitionCategoryByNameMap, +}; diff --git a/reverse_engineering/helpers/sortByName.js b/reverse_engineering/helpers/sortByName.js index 0402b1e..4832d28 100644 --- a/reverse_engineering/helpers/sortByName.js +++ b/reverse_engineering/helpers/sortByName.js @@ -1,14 +1,14 @@ /** - * @import {FieldsOrder, REDefinition} from "../../shared/types/types" + * @import {FieldsOrder, PreProcessedFieldData, REDefinition} from "../../shared/types/types" */ /** * Sorts an array of objects by the name according to the fields order option * * @param {object} params - * @param {REDefinition[]} params.items - The items to sort + * @param {REDefinition[] | PreProcessedFieldData[]} params.items - The items to sort * @param {FieldsOrder} params.fieldsOrder - The fields order - * @returns {REDefinition[]} The sorted items + * @returns {REDefinition[] | PreProcessedFieldData[]} The sorted items */ function sortByName({ items, fieldsOrder }) { if (!Array.isArray(items)) { diff --git a/reverse_engineering/mappers/field.js b/reverse_engineering/mappers/field.js new file mode 100644 index 0000000..719594c --- /dev/null +++ b/reverse_engineering/mappers/field.js @@ -0,0 +1,101 @@ +/** + * @import {FieldDefinitionNode, TypeNode} from "graphql" + * @import {DefinitionNameToTypeNameMap, FieldTypeProperties, PreProcessedFieldData} from "./../../shared/types/types" + */ + +const { mapDirectivesUsage } = require('./directiveUsage'); +const { astNodeKind } = require('../constants/graphqlAST'); +const { BUILT_IN_SCALAR_LIST } = require('../constants/types'); + +/** + * Maps a field + * + * @param {object} params + * @param {FieldDefinitionNode} params.field - The field to map + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map + * @returns {PreProcessedFieldData} The mapped field + */ +function mapField({ field, definitionCategoryByNameMap }) { + const fieldTypeProperties = getTypeProperties({ type: field.type, definitionCategoryByNameMap }); + const sharedProperties = { + name: field.name.value, + fieldDirectives: mapDirectivesUsage({ directives: [...(field.directives || [])] }), + ...fieldTypeProperties, + }; + const description = field.description?.value; + + if ('$ref' in fieldTypeProperties) { + return { + ...sharedProperties, + refDescription: description, + // TODO: add arguments + }; + } + return { + ...sharedProperties, + description, + // TODO: add arguments + }; +} + +/** + * Recursively maps the type properties unwrapping non-null and list types and resolving named types to references + * + * @param {object} params + * @param {TypeNode} params.type - The GraphQL type node to process + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map + * @returns {FieldTypeProperties} JSON schema representation of the type + */ +function getTypeProperties({ type, definitionCategoryByNameMap }) { + // unwrap required type + if (type.kind === astNodeKind.NON_NULL_TYPE) { + const innerType = getTypeProperties({ type: type.type, definitionCategoryByNameMap }); + return { + ...innerType, + required: true, + }; + } + + if (type.kind === astNodeKind.LIST_TYPE) { + const innerType = getTypeProperties({ type: type.type, definitionCategoryByNameMap }); + return { + type: 'List', + items: [innerType], + required: false, + }; + } + + if (type.kind === astNodeKind.NAMED_TYPE) { + const typeName = type.name.value; + const isScalar = isBuiltInScalar({ typeName }); + + if (isScalar) { + return { + type: typeName, + required: false, + }; + } + + const definitionCategoryName = definitionCategoryByNameMap[typeName]; + if (definitionCategoryName) { + return { + '$ref': `#model/definitions/${definitionCategoryName}/${typeName}`, + required: false, + }; + } + } + + // fallback + return { + type: 'string', + required: false, + }; +} + +function isBuiltInScalar({ typeName }) { + return BUILT_IN_SCALAR_LIST.includes(typeName); +} + +module.exports = { + mapField, +}; diff --git a/reverse_engineering/mappers/schema.js b/reverse_engineering/mappers/schema.js index 1d9989d..7d0956c 100644 --- a/reverse_engineering/mappers/schema.js +++ b/reverse_engineering/mappers/schema.js @@ -27,8 +27,13 @@ function getMappedSchema({ schemaItems, graphName, logger, fieldsOrder }) { rootSchemaNode: findNodesByKind({ nodes: schemaItems, kind: Kind.SCHEMA_DEFINITION })[0], graphName, }); + const rootTypeNames = [ + container.schemaRootTypes?.rootQuery || 'Query', + container.schemaRootTypes?.rootMutation || 'Mutation', + container.schemaRootTypes?.rootSubscription || 'Subscription', + ]; - const typeDefinitions = getTypeDefinitions({ typeDefinitions: schemaItems, fieldsOrder }); + const typeDefinitions = getTypeDefinitions({ typeDefinitions: schemaItems, fieldsOrder, rootTypeNames }); return [ // TODO: remove test collection diff --git a/reverse_engineering/mappers/typeDefinitions/objectType.js b/reverse_engineering/mappers/typeDefinitions/objectType.js new file mode 100644 index 0000000..6b07b5d --- /dev/null +++ b/reverse_engineering/mappers/typeDefinitions/objectType.js @@ -0,0 +1,58 @@ +/** + * @import {ObjectTypeDefinitionNode} from "graphql" + * @import {DefinitionNameToTypeNameMap, FieldsOrder, REObjectTypeDefinition, REPropertiesSchema} from "../../../shared/types/types" + */ + +const { sortByName } = require('../../helpers/sortByName'); +const { mapDirectivesUsage } = require('../directiveUsage'); +const { mapField } = require('../field'); + +/** + * Maps object type definitions + * + * @param {object} params + * @param {ObjectTypeDefinitionNode[]} params.objectTypes - The object types + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map + * @param {FieldsOrder} params.fieldsOrder - The fields order + * @returns {REObjectTypeDefinition[]} The mapped object type definitions + */ +function getObjectTypeDefinitions({ objectTypes = [], definitionCategoryByNameMap, fieldsOrder }) { + return objectTypes.map(objectType => mapObjectType({ objectType, definitionCategoryByNameMap, fieldsOrder })); +} + +/** + * Maps a single object type definition + * + * @param {object} params + * @param {ObjectTypeDefinitionNode} params.objectType - The object type to map + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map + * @param {FieldsOrder} params.fieldsOrder - The fields order + * @returns {REObjectTypeDefinition} The mapped object type definition + */ +function mapObjectType({ objectType, definitionCategoryByNameMap, fieldsOrder }) { + const properties = objectType.fields + ? objectType.fields.map(field => mapField({ field, definitionCategoryByNameMap })) + : []; + const required = properties.filter(property => property.required).map(property => property.name); + const convertedProperties = sortByName({ items: properties, fieldsOrder }).reduce( + (acc, property) => { + acc[property.name] = property; + return acc; + }, + /** @type {REPropertiesSchema} */ {}, + ); + + return { + type: 'object', + name: objectType.name.value, + properties: convertedProperties, + required, + description: objectType.description?.value || '', + typeDirectives: mapDirectivesUsage({ directives: [...(objectType.directives || [])] }), + // TODO: add interfaces + }; +} + +module.exports = { + getObjectTypeDefinitions, +}; diff --git a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js index 712ed26..cf78b2e 100644 --- a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js +++ b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js @@ -1,13 +1,15 @@ /** * @import {DefinitionNode} from "graphql" - * @import {REDirectiveDefinition, REDefinitionsSchema, FieldsOrder, RECustomScalarDefinition, REDefinition, REModelDefinitionsSchema, DefinitionREStructure, DirectiveStructureType, ScalarStructureType} from "../../../shared/types/types" + * @import {REDirectiveDefinition, REDefinitionsSchema, FieldsOrder, RECustomScalarDefinition, REDefinition, REModelDefinitionsSchema, DefinitionREStructure, DirectiveStructureType, ScalarStructureType, REObjectTypeDefinition, ObjectStructureType, PreProcessedFieldData} from "../../../shared/types/types" */ const { astNodeKind } = require('../../constants/graphqlAST'); const { findNodesByKind } = require('../../helpers/findNodesByKind'); +const { getDefinitionCategoryByNameMap } = require('../../helpers/getDefinitionCategoryByNameMap'); const { sortByName } = require('../../helpers/sortByName'); const { getCustomScalarTypeDefinitions } = require('./customScalar'); const { getDirectiveTypeDefinitions } = require('./directive'); +const { getObjectTypeDefinitions } = require('./objectType'); /** * Gets the type definitions structure @@ -15,17 +17,29 @@ const { getDirectiveTypeDefinitions } = require('./directive'); * @param {object} params * @param {DefinitionNode[]} params.typeDefinitions - The type definitions nodes * @param {FieldsOrder} params.fieldsOrder - The fields order + * @param {string[]} params.rootTypeNames - The root type names * @returns {REModelDefinitionsSchema} The mapped type definitions */ -function getTypeDefinitions({ typeDefinitions, fieldsOrder }) { +function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames }) { + const definitionCategoryByNameMap = getDefinitionCategoryByNameMap({ nodes: typeDefinitions }); + const directives = getDirectiveTypeDefinitions({ directives: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.DIRECTIVE_DEFINITION }), }); const customScalars = getCustomScalarTypeDefinitions({ customScalars: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.SCALAR_TYPE_DEFINITION }), }); + const objectDefinitionNodes = findNodesByKind({ + nodes: typeDefinitions, + kind: astNodeKind.OBJECT_TYPE_DEFINITION, + }).filter(node => !rootTypeNames.includes(node.name.value)); + const objectTypes = getObjectTypeDefinitions({ + objectTypes: objectDefinitionNodes, + definitionCategoryByNameMap, + fieldsOrder, + }); - const definitions = getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars }); + const definitions = getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, objectTypes }); return definitions; } @@ -37,9 +51,10 @@ function getTypeDefinitions({ typeDefinitions, fieldsOrder }) { * @param {FieldsOrder} params.fieldsOrder - The fields order * @param {REDirectiveDefinition[]} params.directives - The directive definitions * @param {RECustomScalarDefinition[]} params.customScalars - The custom scalar definitions + * @param {REObjectTypeDefinition[]} params.objectTypes - The object type definitions * @returns {REModelDefinitionsSchema} The type definitions structure */ -function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars }) { +function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, objectTypes }) { const definitions = { ['Directives']: /** @type {DirectiveStructureType} */ ( getDefinitionCategoryStructure({ @@ -55,6 +70,13 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars }) properties: customScalars, }) ), + ['Objects']: /** @type {ObjectStructureType} */ ( + getDefinitionCategoryStructure({ + fieldsOrder, + subtype: 'object', + properties: objectTypes, + }) + ), }; return { @@ -72,22 +94,33 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars }) * @returns {DefinitionREStructure} The definition category structure */ function getDefinitionCategoryStructure({ fieldsOrder, subtype, properties }) { - const sortedFields = sortByName({ items: properties, fieldsOrder }); - return { type: 'type', subtype, structureType: true, - properties: sortedFields.reduce( - (acc, prop) => { - acc[prop.name] = prop; - return acc; - }, - /** @type {REDefinitionsSchema} */ {}, - ), + properties: handleProperties({ properties, fieldsOrder }), }; } +/** + * Processes properties, sorting them by name and converting them to JSON schema format + * + * @param {object} params + * @param {REDefinition[]} params.properties - The properties to process + * @param {FieldsOrder} params.fieldsOrder - The fields order + * @returns {object} The processed properties as a map of name to property + */ +function handleProperties({ properties, fieldsOrder }) { + const sortedFields = sortByName({ items: properties, fieldsOrder }); + + return sortedFields.reduce((acc, prop) => { + const processedProp = { ...prop }; + + acc[processedProp.name] = processedProp; + return acc; + }, {}); +} + module.exports = { getTypeDefinitions, }; diff --git a/shared/types/fe.d.ts b/shared/types/fe.d.ts index 3a070c6..e96a248 100644 --- a/shared/types/fe.d.ts +++ b/shared/types/fe.d.ts @@ -1,9 +1,12 @@ import { + Argument, ContainerSchemaRootTypes, CustomScalarDefinition, DirectiveDefinition, DirectiveLocations, DirectivePropertyData, + FieldSchema, + ObjectLikeDefinition, } from './shared'; export type FEStatement = { @@ -50,61 +53,7 @@ export type FEEnumDefinitionsSchema = Record; // Object type definition export type FEObjectLikeDefinitionsSchema = Record; -export type FEObjectLikeDefinition = { - description?: string; // Description of the object type - isActivated?: boolean; // If the object type is activated - implementsInterfaces?: ImplementsInterface[]; // Interfaces that the object type implements - typeDirectives?: DirectivePropertyData[]; // Directives for the type - properties: Record; // Properties of the object type - required?: boolean; -}; - -// Field data type -export type FieldData = RegularFieldData | ReferenceFieldData; - -export type FieldSchema = Record - -export type ArrayItems = ArrayItem | ArrayItem[]; - -type RegularFieldData = { - type: string; // Type of the field - isActivated?: boolean; // If the field is activated - description?: string; // Description of the field - fieldDirectives?: DirectivePropertyData[]; // Directives for the field - items?: ArrayItems; // Items of the List type - arguments?: Argument[]; // Arguments of the field - default?: string; // Default value of the field -}; - -type ReferenceFieldData = { - $ref: string; // Reference path to the type definition - isActivated?: boolean; // If the field is activated - refDescription?: string; // Description of the reference - fieldDirectives?: DirectivePropertyData[]; // Directives for the field - arguments?: Argument[]; // Arguments of the field - default?: string; // Default value of the reference -}; - -export type ArrayItem = FieldData & { - required?: boolean; // If the array item is required -}; - -// Field arguments -type ArgumentListItem = { - type?: string; - required?: boolean; -}; - -export type Argument = { - id: string; - type: string; - name: string; - default?: string; - description?: string; - directives?: DirectivePropertyData[]; - required?: boolean; - listItems?: ArgumentListItem[]; -}; +export type FEObjectLikeDefinition = ObjectLikeDefinition; export type ArgumentsResultStatement = { argumentsStatement: string; // The formatted arguments string. diff --git a/shared/types/re.d.ts b/shared/types/re.d.ts index 8639017..cd45f55 100644 --- a/shared/types/re.d.ts +++ b/shared/types/re.d.ts @@ -2,6 +2,8 @@ import { ContainerSchemaRootTypes, CustomScalarDefinition, DirectiveDefinition, + FieldData, + ObjectLikeDefinition, StructuredDirective, } from './shared'; @@ -118,17 +120,19 @@ export type REDefinitionsSchema = REDirectiveDefinitionsSchema | RECustomScalarD export type REDirectiveDefinitionsSchema = Record; export type RECustomScalarDefinitionsSchema = Record; +export type REObjectDefinitionsSchema = Record; -export type REDefinition = RECustomScalarDefinition | REDirectiveDefinition; +export type REDefinition = RECustomScalarDefinition | REDirectiveDefinition | REObjectTypeDefinition; export type REModelDefinitionsSchema = { definitions: { Directives: DirectiveStructureType; Scalars: ScalarStructureType; + Objects: ObjectStructureType; } } -export type DefinitionREStructure = DirectiveStructureType | ScalarStructureType; +export type DefinitionREStructure = DirectiveStructureType | ScalarStructureType | ObjectStructureType; type StructureType = { type: 'type'; @@ -149,5 +153,46 @@ export type RECustomScalarDefinition = CustomScalarDefinition & { + subtype: 'object'; +} + +type REImplementsInterface = { + name: string; // Name of the interface +}; + +export type REPropertiesSchema = Record; +type REObjectLikeDefinition = ObjectLikeDefinition; + +export type REObjectTypeDefinition = REObjectLikeDefinition & { + type: 'object'; + name: string; +} + +export type PreProcessedFieldData = FieldData & FieldTypeProperties & { + name: string; +} + +export type FieldTypeProperties = RegularFieldTypeProperties | ArrayFieldTypeProperties | ReferenceFieldTypeProperties; + +export type RegularFieldTypeProperties = { + type: string; + required: boolean; +} + +export type ArrayFieldTypeProperties = { + type: 'List'; + items?: [FieldTypeProperties]; + required: boolean; +}; + +export type ReferenceFieldTypeProperties = { + $ref: string; + required: boolean; +}; + +export type DefinitionTypeName = 'Scalars' | 'Enums' | 'Objects' | 'Interfaces' | 'Unions' | 'Input objects' | 'Directives'; +export type DefinitionNameToTypeNameMap = Record; + export type TestConnectionCallback = (err?: Error | unknown) => void; export type DisconnectCallback = TestConnectionCallback; diff --git a/shared/types/shared.d.ts b/shared/types/shared.d.ts index bc3ea35..5b5f65d 100644 --- a/shared/types/shared.d.ts +++ b/shared/types/shared.d.ts @@ -67,3 +67,60 @@ export type DirectiveDefinition = { arguments?: T[]; directiveLocations: D; } + +export type ObjectLikeDefinition = { + description?: string; // Description of the object type + isActivated?: boolean; // If the object type is activated + implementsInterfaces?: Interfaces; // Interfaces that the object type implements + typeDirectives?: DirectivePropertyData[]; // Directives for the type + properties: Record; // Properties of the object type + required?: string[]; +}; + +// Field data type +export type FieldData = RegularFieldData | ReferenceFieldData; + +export type FieldSchema = Record + +export type ArrayItems = ArrayItem | ArrayItem[]; + +type RegularFieldData = { + type: string; // Type of the field + isActivated?: boolean; // If the field is activated + description?: string; // Description of the field + fieldDirectives?: DirectivePropertyData[]; // Directives for the field + items?: ArrayItems; // Items of the List type + arguments?: Argument[]; // Arguments of the field + default?: string; // Default value of the field +}; + +type ReferenceFieldData = { + $ref: string; // Reference path to the type definition + isActivated?: boolean; // If the field is activated + refDescription?: string; // Description of the reference + fieldDirectives?: DirectivePropertyData[]; // Directives for the field + arguments?: Argument[]; // Arguments of the field + default?: string; // Default value of the reference +}; + +export type ArrayItem = FieldData & { + required?: boolean; // If the array item is required + $ref?: string; // Reference path to the type definition +}; + +// Field arguments +type ArgumentListItem = { + type?: string; + required?: boolean; +}; + +export type Argument = { + id: string; + type: string; + name: string; + default?: string; + description?: string; + directives?: DirectivePropertyData[]; + required?: boolean; + listItems?: ArgumentListItem[]; +}; From 6a021e961e238fa296bb2b827ec17ecb004b7e47 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Fri, 21 Mar 2025 19:28:52 +0200 Subject: [PATCH 2/7] update types --- reverse_engineering/mappers/field.js | 7 +++++ .../typeDefinitions/typeDefinitions.js | 26 ++++++------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/reverse_engineering/mappers/field.js b/reverse_engineering/mappers/field.js index 719594c..337735f 100644 --- a/reverse_engineering/mappers/field.js +++ b/reverse_engineering/mappers/field.js @@ -92,6 +92,13 @@ function getTypeProperties({ type, definitionCategoryByNameMap }) { }; } +/** + * Checks if a type name is a built-in scalar + * + * @param {object} params + * @param {string} params.typeName - The type name to check + * @returns {boolean} True if the type is a built-in scalar, false otherwise + */ function isBuiltInScalar({ typeName }) { return BUILT_IN_SCALAR_LIST.includes(typeName); } diff --git a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js index cf78b2e..07cdcba 100644 --- a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js +++ b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js @@ -94,31 +94,21 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, o * @returns {DefinitionREStructure} The definition category structure */ function getDefinitionCategoryStructure({ fieldsOrder, subtype, properties }) { - return { - type: 'type', - subtype, - structureType: true, - properties: handleProperties({ properties, fieldsOrder }), - }; -} - -/** - * Processes properties, sorting them by name and converting them to JSON schema format - * - * @param {object} params - * @param {REDefinition[]} params.properties - The properties to process - * @param {FieldsOrder} params.fieldsOrder - The fields order - * @returns {object} The processed properties as a map of name to property - */ -function handleProperties({ properties, fieldsOrder }) { const sortedFields = sortByName({ items: properties, fieldsOrder }); - return sortedFields.reduce((acc, prop) => { + const convertedProperties = sortedFields.reduce((acc, prop) => { const processedProp = { ...prop }; acc[processedProp.name] = processedProp; return acc; }, {}); + + return { + type: 'type', + subtype, + structureType: true, + properties: convertedProperties, + }; } module.exports = { From 5dc1230b3553f5e4ba9eb735b207d9bf599b649f Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Fri, 21 Mar 2025 19:43:06 +0200 Subject: [PATCH 3/7] add field mapper tests --- .../mappers/typeDefinitions/field.spec.js | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 test/reverse_engineering/mappers/typeDefinitions/field.spec.js diff --git a/test/reverse_engineering/mappers/typeDefinitions/field.spec.js b/test/reverse_engineering/mappers/typeDefinitions/field.spec.js new file mode 100644 index 0000000..66675a6 --- /dev/null +++ b/test/reverse_engineering/mappers/typeDefinitions/field.spec.js @@ -0,0 +1,271 @@ +const { describe, it, mock, afterEach } = require('node:test'); +const assert = require('assert'); + +// Mock dependencies +const mapDirectivesUsageMock = mock.fn(() => []); + +mock.module('../../../../reverse_engineering/mappers/directiveUsage', { + namedExports: { + mapDirectivesUsage: mapDirectivesUsageMock, + }, +}); + +const astNodeKindMock = { + NAMED_TYPE: 'NamedType', + NON_NULL_TYPE: 'NonNullType', + LIST_TYPE: 'ListType', +}; + +const { mapField } = require('../../../../reverse_engineering/mappers/field'); + +describe('field', () => { + afterEach(() => { + mapDirectivesUsageMock.mock.resetCalls(); + }); + + describe('mapField', () => { + it('should map basic field with scalar type', () => { + const field = { + name: { value: 'name' }, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'String' }, + }, + directives: [], + }; + const definitionCategoryByNameMap = {}; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'name', + type: 'String', + required: false, + fieldDirectives: [], + description: undefined, + }); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + }); + + it('should map field with description', () => { + const field = { + name: { value: 'name' }, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'String' }, + }, + description: { value: 'User name' }, + directives: [], + }; + const definitionCategoryByNameMap = {}; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'name', + type: 'String', + required: false, + fieldDirectives: [], + description: 'User name', + }); + }); + + it('should map field with directives', () => { + const mockDirectives = [{ name: { value: 'deprecated' }, arguments: [] }]; + const mockMappedDirectives = [{ directiveName: '@deprecated', rawArgumentValues: '' }]; + mapDirectivesUsageMock.mock.mockImplementationOnce(() => mockMappedDirectives); + + const field = { + name: { value: 'oldField' }, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'String' }, + }, + directives: mockDirectives, + }; + const definitionCategoryByNameMap = {}; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'oldField', + type: 'String', + required: false, + fieldDirectives: mockMappedDirectives, + description: undefined, + }); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], { + directives: mockDirectives, + }); + }); + + it('should map field with reference type', () => { + const field = { + name: { value: 'user' }, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'User' }, + }, + description: { value: 'The user' }, + directives: [], + }; + const definitionCategoryByNameMap = { + 'User': 'Objects', + }; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'user', + $ref: '#model/definitions/Objects/User', + required: false, + fieldDirectives: [], + refDescription: 'The user', + }); + }); + + it('should map field with required type', () => { + const field = { + name: { value: 'id' }, + type: { + kind: astNodeKindMock.NON_NULL_TYPE, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'ID' }, + }, + }, + directives: [], + }; + const definitionCategoryByNameMap = {}; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'id', + type: 'ID', + required: true, + fieldDirectives: [], + description: undefined, + }); + }); + + it('should map field with list type', () => { + const field = { + name: { value: 'friends' }, + type: { + kind: astNodeKindMock.LIST_TYPE, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'User' }, + }, + }, + directives: [], + }; + const definitionCategoryByNameMap = { + 'User': 'Objects', + }; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'friends', + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/User', + required: false, + }, + ], + required: false, + fieldDirectives: [], + description: undefined, + }); + }); + + it('should map field with required list of required items', () => { + const field = { + name: { value: 'requiredFriends' }, + type: { + kind: astNodeKindMock.NON_NULL_TYPE, + type: { + kind: astNodeKindMock.LIST_TYPE, + type: { + kind: astNodeKindMock.NON_NULL_TYPE, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'User' }, + }, + }, + }, + }, + directives: [], + }; + const definitionCategoryByNameMap = { + 'User': 'Objects', + }; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'requiredFriends', + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/User', + required: true, + }, + ], + required: true, + fieldDirectives: [], + description: undefined, + }); + }); + + it('should handle undefined directives', () => { + const field = { + name: { value: 'name' }, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'String' }, + }, + // directives property is omitted + }; + const definitionCategoryByNameMap = {}; + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'name', + type: 'String', + required: false, + fieldDirectives: [], + description: undefined, + }); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []); + }); + + it('should fallback to string type for unknown named types', () => { + const field = { + name: { value: 'customType' }, + type: { + kind: astNodeKindMock.NAMED_TYPE, + name: { value: 'UnknownType' }, + }, + directives: [], + }; + const definitionCategoryByNameMap = {}; // UnknownType not in map + + const result = mapField({ field, definitionCategoryByNameMap }); + + assert.deepStrictEqual(result, { + name: 'customType', + type: 'string', // Fallback to string + required: false, + fieldDirectives: [], + description: undefined, + }); + }); + }); +}); From abd9ba90b5e5a5b3a8794d77035619315be27874 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Fri, 21 Mar 2025 20:02:00 +0200 Subject: [PATCH 4/7] add object type tests --- .../typeDefinitions/objectType.spec.js | 328 ++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js diff --git a/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js b/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js new file mode 100644 index 0000000..495bdbe --- /dev/null +++ b/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js @@ -0,0 +1,328 @@ +const { describe, it, mock, afterEach } = require('node:test'); +const assert = require('assert'); + +// Mock dependencies +const sortByNameMock = mock.fn(({ items }) => items); +mock.module('../../../../reverse_engineering/helpers/sortByName', { + namedExports: { + sortByName: sortByNameMock, + }, +}); + +const mapDirectivesUsageMock = mock.fn(() => []); +mock.module('../../../../reverse_engineering/mappers/directiveUsage', { + namedExports: { + mapDirectivesUsage: mapDirectivesUsageMock, + }, +}); + +const mapFieldMock = mock.fn(({ field }) => ({ + name: field.name.value, + required: field.type.kind === 'NON_NULL_TYPE', + // Add other properties that mapField would return +})); +mock.module('../../../../reverse_engineering/mappers/field', { + namedExports: { + mapField: mapFieldMock, + }, +}); + +const { getObjectTypeDefinitions } = require('../../../../reverse_engineering/mappers/typeDefinitions/objectType'); + +describe('getObjectTypeDefinitions', () => { + afterEach(() => { + sortByNameMock.mock.resetCalls(); + mapDirectivesUsageMock.mock.resetCalls(); + mapFieldMock.mock.resetCalls(); + }); + + it('should return an empty array when no object types are provided', () => { + const result = getObjectTypeDefinitions({ + objectTypes: [], + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + assert.deepStrictEqual(result, []); + }); + + it('should correctly map a simple object type with no fields', () => { + const mockObjectType = { + name: { value: 'EmptyType' }, + fields: [], + directives: [], + }; + + const expected = [ + { + type: 'object', + name: 'EmptyType', + properties: {}, + required: [], + description: '', + typeDirectives: [], + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.strictEqual(sortByNameMock.mock.calls.length, 1); + assert.strictEqual(mapFieldMock.mock.calls.length, 0); + }); + + it('should correctly map an object type with fields', () => { + const mockObjectType = { + name: { value: 'User' }, + description: { value: 'A user object' }, + fields: [ + { + name: { value: 'id' }, + type: { kind: 'NON_NULL_TYPE' }, + directives: [], + }, + { + name: { value: 'name' }, + type: { kind: 'NAMED_TYPE' }, + directives: [], + }, + ], + directives: [], + }; + + // Setup the mapField mock to return expected values + mapFieldMock.mock.mockImplementation(({ field }) => ({ + name: field.name.value, + required: field.type.kind === 'NON_NULL_TYPE', + })); + + // Expected result with the properties based on the mocked mapField responses + const expected = [ + { + type: 'object', + name: 'User', + properties: { + id: { name: 'id', required: true }, + name: { name: 'name', required: false }, + }, + required: ['id'], + description: 'A user object', + typeDirectives: [], + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.strictEqual(sortByNameMock.mock.calls.length, 1); + assert.strictEqual(mapFieldMock.mock.calls.length, 2); + }); + + it('should correctly map an object type with directives', () => { + const mockDirectiveResult = [{ directiveName: '@auth', rawArgumentValues: 'requires: "ADMIN"' }]; + mapDirectivesUsageMock.mock.mockImplementationOnce(() => mockDirectiveResult); + + const mockObjectType = { + name: { value: 'AdminType' }, + fields: [], + directives: [ + { + name: { value: 'auth' }, + arguments: [{ name: { value: 'requires' }, value: { value: 'ADMIN' } }], + }, + ], + }; + + const expected = [ + { + type: 'object', + name: 'AdminType', + properties: {}, + required: [], + description: '', + typeDirectives: mockDirectiveResult, + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], { + directives: mockObjectType.directives, + }); + }); + + it('should correctly handle fields order', () => { + const mockObjectType = { + name: { value: 'OrderedType' }, + fields: [ + { name: { value: 'fieldB' }, type: { kind: 'NAMED_TYPE' }, directives: [] }, + { name: { value: 'fieldA' }, type: { kind: 'NAMED_TYPE' }, directives: [] }, + { name: { value: 'fieldC' }, type: { kind: 'NAMED_TYPE' }, directives: [] }, + ], + directives: [], + }; + + mapFieldMock.mock.mockImplementation(({ field }) => ({ + name: field.name.value, + required: false, + })); + + // Test both fieldsOrder options + const testCases = [ + { + fieldsOrder: 'alphabetical', + expectedOrder: ['fieldA', 'fieldB', 'fieldC'], // Alphabetical order + }, + { + fieldsOrder: 'field', + expectedOrder: ['fieldB', 'fieldA', 'fieldC'], // Original order + }, + ]; + + for (const testCase of testCases) { + // Reset mocks before each test case + sortByNameMock.mock.resetCalls(); + mapFieldMock.mock.resetCalls(); + + // Mock sortByName to simulate behavior based on fieldsOrder value + if (testCase.fieldsOrder === 'alphabetical') { + // For 'alphabetical', sort alphabetically + sortByNameMock.mock.mockImplementationOnce(({ items }) => { + return [...items].sort((a, b) => a.name.localeCompare(b.name)); + }); + } else { + // For 'field', maintain original order + sortByNameMock.mock.mockImplementationOnce(({ items }) => items); + } + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: {}, + fieldsOrder: testCase.fieldsOrder, + }); + + // Check that sortByName was called correctly + assert.strictEqual(sortByNameMock.mock.calls.length, 1); + assert.strictEqual(sortByNameMock.mock.calls[0].arguments[0].fieldsOrder, testCase.fieldsOrder); + + // Verify the result structure + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].name, 'OrderedType'); + + const propertyNames = Object.keys(result[0].properties); + + // Verify the properties are in the expected order for this test case + assert.deepStrictEqual( + propertyNames, + testCase.expectedOrder, + `Field order should be ${testCase.expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`, + ); + } + }); + + it('should correctly map multiple object types', () => { + const mockObjectTypes = [ + { + name: { value: 'Type1' }, + fields: [], + directives: [], + }, + { + name: { value: 'Type2' }, + fields: [], + directives: [], + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: mockObjectTypes, + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + + assert.strictEqual(result.length, 2); + assert.strictEqual(result[0].name, 'Type1'); + assert.strictEqual(result[1].name, 'Type2'); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 2); + }); + + it('should handle undefined fields', () => { + const mockObjectType = { + name: { value: 'TypeWithoutFields' }, + // fields is undefined + directives: [], + }; + + const expected = [ + { + type: 'object', + name: 'TypeWithoutFields', + properties: {}, + required: [], + description: '', + typeDirectives: [], + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + + // Verify no fields were processed + assert.strictEqual(mapFieldMock.mock.calls.length, 0); + + // Verify sortByName was still called (but with empty array) + assert.strictEqual(sortByNameMock.mock.calls.length, 1); + assert.deepStrictEqual(sortByNameMock.mock.calls[0].arguments[0].items, []); + }); + + it('should handle undefined directives', () => { + const mockObjectType = { + name: { value: 'TypeWithoutDirectives' }, + fields: [], + // directives is undefined + }; + + const expected = [ + { + type: 'object', + name: 'TypeWithoutDirectives', + properties: {}, + required: [], + description: '', + typeDirectives: [], // We expect an empty array since our mock returns [] + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: {}, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + + // Verify the mapDirectivesUsage was called with empty directives array + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []); + }); +}); From c3923f4a126f533b2d938bed0d530b044b8e97f2 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Fri, 21 Mar 2025 20:03:07 +0200 Subject: [PATCH 5/7] move field mapper test for consistent files structure --- .../mappers/{typeDefinitions => }/field.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename test/reverse_engineering/mappers/{typeDefinitions => }/field.spec.js (97%) diff --git a/test/reverse_engineering/mappers/typeDefinitions/field.spec.js b/test/reverse_engineering/mappers/field.spec.js similarity index 97% rename from test/reverse_engineering/mappers/typeDefinitions/field.spec.js rename to test/reverse_engineering/mappers/field.spec.js index 66675a6..4d60cef 100644 --- a/test/reverse_engineering/mappers/typeDefinitions/field.spec.js +++ b/test/reverse_engineering/mappers/field.spec.js @@ -4,7 +4,7 @@ const assert = require('assert'); // Mock dependencies const mapDirectivesUsageMock = mock.fn(() => []); -mock.module('../../../../reverse_engineering/mappers/directiveUsage', { +mock.module('../../../reverse_engineering/mappers/directiveUsage', { namedExports: { mapDirectivesUsage: mapDirectivesUsageMock, }, @@ -16,7 +16,7 @@ const astNodeKindMock = { LIST_TYPE: 'ListType', }; -const { mapField } = require('../../../../reverse_engineering/mappers/field'); +const { mapField } = require('../../../reverse_engineering/mappers/field'); describe('field', () => { afterEach(() => { From 581354e7ffd4b703f16fbb2d4dc88fd1d91b9d28 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Mon, 24 Mar 2025 16:15:56 +0200 Subject: [PATCH 6/7] update types --- .../mappers/fieldDefaultValue.js | 4 +-- forward_engineering/mappers/fields.js | 12 ++++---- shared/types/fe.d.ts | 4 +-- shared/types/re.d.ts | 8 +++--- shared/types/shared.d.ts | 28 +++++++++---------- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/forward_engineering/mappers/fieldDefaultValue.js b/forward_engineering/mappers/fieldDefaultValue.js index 6b1169c..97b985d 100644 --- a/forward_engineering/mappers/fieldDefaultValue.js +++ b/forward_engineering/mappers/fieldDefaultValue.js @@ -1,12 +1,12 @@ /** - * @import {FieldData} from "../../shared/types/types" + * @import {DirectivePropertyData, FieldData} from "../../shared/types/types" */ /** * Generates the default value statement for a field. * * @param {object} param0 - * @param {FieldData} param0.field - The field object. + * @param {FieldData} param0.field - The field object. * @returns {string} - The default value statement. */ function getFieldDefaultValueStatement({ field }) { diff --git a/forward_engineering/mappers/fields.js b/forward_engineering/mappers/fields.js index 39a4e4e..ab2d49a 100644 --- a/forward_engineering/mappers/fields.js +++ b/forward_engineering/mappers/fields.js @@ -1,5 +1,5 @@ /** - * @import {FEStatement, BaseGetFieldParams, GetFieldsParams, ArrayItems, FieldData, ArrayItem, IdToNameMap} from "../../shared/types/types" + * @import {FEStatement, BaseGetFieldParams, GetFieldsParams, ArrayItems, FieldData, ArrayItem, IdToNameMap, DirectivePropertyData} from "../../shared/types/types" */ const { joinInlineStatements } = require('../helpers/feStatementJoinHelper'); @@ -30,7 +30,7 @@ function getFields({ fields = {}, requiredFields = [], definitionsIdToNameMap, a /** * @param {object} params - * @param {FieldData} params.fieldData + * @param {FieldData} params.fieldData * @returns {string} */ function getFieldDescription({ fieldData }) { @@ -48,7 +48,7 @@ function getFieldDescription({ fieldData }) { * * @param {object} param0 * @param {string} param0.name - The name of the field. - * @param {FieldData} param0.fieldData - The field data object. + * @param {FieldData} param0.fieldData - The field data object. * @param {boolean} param0.required - Indicates if the field is required. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @param {boolean} param0.addArguments - Indicates if arguments should be added. @@ -79,7 +79,7 @@ function mapField({ name, fieldData, required, definitionsIdToNameMap, addArgume * Gets the field type. * * @param {object} param0 - * @param {FieldData} param0.field - The field data object. + * @param {FieldData} param0.field - The field data object. * @param {boolean} [param0.required] - Indicates if the field is required. * @returns {string} - The field type. */ @@ -116,8 +116,8 @@ function getFieldType({ field, required }) { * Gets the field from array items. * * @param {object} param0 - * @param {ArrayItems} [param0.items] - The array items. - * @returns {ArrayItem | undefined} - The field. + * @param {ArrayItems} [param0.items] - The array items. + * @returns {ArrayItem | undefined} - The field. */ function getFieldFromArrayItems({ items }) { if (Array.isArray(items)) { diff --git a/shared/types/fe.d.ts b/shared/types/fe.d.ts index 2457007..c2d354a 100644 --- a/shared/types/fe.d.ts +++ b/shared/types/fe.d.ts @@ -47,7 +47,7 @@ export type FEEnumDefinitionsSchema = Record; // Object type definition export type FEObjectLikeDefinitionsSchema = Record; -export type FEObjectLikeDefinition = ObjectLikeDefinition; +export type FEObjectLikeDefinition = ObjectLikeDefinition; export type ArgumentsResultStatement = { argumentsStatement: string; // The formatted arguments string. @@ -181,7 +181,7 @@ export type ValidateScriptCallback = ( ) => void; export type BaseGetFieldParams = { - fields?: FieldSchema; // The fields to get + fields?: FieldSchema; // The fields to get requiredFields?: string[]; // The required fields list definitionsIdToNameMap: IdToNameMap; // The definitions id to name map }; diff --git a/shared/types/re.d.ts b/shared/types/re.d.ts index 2bdf81c..9b02b0d 100644 --- a/shared/types/re.d.ts +++ b/shared/types/re.d.ts @@ -176,12 +176,12 @@ export type ObjectStructureType = StructureType & { subtype: 'object'; }; -type REImplementsInterface = { - name: string; // Name of the interface +export type REImplementsInterface = { + interface: string; // Name of the interface }; -export type REPropertiesSchema = Record; -type REObjectLikeDefinition = ObjectLikeDefinition; +export type REPropertiesSchema = Record>; +type REObjectLikeDefinition = ObjectLikeDefinition; export type REObjectTypeDefinition = REObjectLikeDefinition & { type: 'object'; diff --git a/shared/types/shared.d.ts b/shared/types/shared.d.ts index a864fbf..a3a3beb 100644 --- a/shared/types/shared.d.ts +++ b/shared/types/shared.d.ts @@ -47,7 +47,7 @@ type RawDirective = { rawDirective: string; // Raw directive string }; -type StructuredDirective = { +export type StructuredDirective = { directiveFormat: 'Structured'; // Format of the directive directiveName: string; // Name of a built-in directive or GUID of a custom directive argumentValueFormat: 'Raw'; // Format of the argument values @@ -68,42 +68,42 @@ export type DirectiveDefinition = { directiveLocations: D; }; -export type ObjectLikeDefinition = { +export type ObjectLikeDefinition = { description?: string; // Description of the object type isActivated?: boolean; // If the object type is activated - implementsInterfaces?: Interfaces; // Interfaces that the object type implements - typeDirectives?: DirectivePropertyData[]; // Directives for the type - properties: Record; // Properties of the object type + implementsInterfaces?: Interface[]; // Interfaces that the object type implements + typeDirectives?: DirectiveUsage[]; // Directives for the type + properties: Record>; // Properties of the object type required?: string[]; }; // Field data type -export type FieldData = RegularFieldData | ReferenceFieldData; +export type FieldData = RegularFieldData | ReferenceFieldData; -export type FieldSchema = Record; +export type FieldSchema = Record>; -export type ArrayItems = ArrayItem | ArrayItem[]; +export type ArrayItems = ArrayItem | ArrayItem[]; -type RegularFieldData = { +type RegularFieldData = { type: string; // Type of the field isActivated?: boolean; // If the field is activated description?: string; // Description of the field - fieldDirectives?: DirectivePropertyData[]; // Directives for the field - items?: ArrayItems; // Items of the List type + fieldDirectives?: DirectiveUsage[]; // Directives for the field + items?: ArrayItems; // Items of the List type arguments?: Argument[]; // Arguments of the field default?: string; // Default value of the field }; -type ReferenceFieldData = { +type ReferenceFieldData = { $ref: string; // Reference path to the type definition isActivated?: boolean; // If the field is activated refDescription?: string; // Description of the reference - fieldDirectives?: DirectivePropertyData[]; // Directives for the field + fieldDirectives?: DirectiveUsage[]; // Directives for the field arguments?: Argument[]; // Arguments of the field default?: string; // Default value of the reference }; -export type ArrayItem = FieldData & { +export type ArrayItem = FieldData & { required?: boolean; // If the array item is required $ref?: string; // Reference path to the type definition }; From 73271a36fd0d3fc5b90fc1b4fbad350db78a4c46 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Mon, 24 Mar 2025 16:17:36 +0200 Subject: [PATCH 7/7] update types --- shared/types/re.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/types/re.d.ts b/shared/types/re.d.ts index 9b02b0d..cf67764 100644 --- a/shared/types/re.d.ts +++ b/shared/types/re.d.ts @@ -188,7 +188,7 @@ export type REObjectTypeDefinition = REObjectLikeDefinition & { name: string; }; -export type PreProcessedFieldData = FieldData & +export type PreProcessedFieldData = FieldData & FieldTypeProperties & { name: string; };