Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions forward_engineering/mappers/fieldDefaultValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function getFieldDefaultValueStatement({ field }) {
* Formats the default value for a field.
*
* @param {object} param0
* @param {string} [param0.defaultValue] - The default value.
* @param {FieldData<DirectivePropertyData>['default']} [param0.defaultValue] - The default value.
* @param {string} param0.fieldType - The type of the field.
* @returns {string} - The formatted default value.
*/
Expand Down Expand Up @@ -84,7 +84,7 @@ function isComplexDefaultValue({ defaultValue }) {
/**
* Checks if a value is present (not undefined or empty).
*
* @param {string} [value] - The value to check.
* @param {FieldData<DirectivePropertyData>['default']} [value] - The value to check.
* @returns {boolean} - True if the value is present, false otherwise.
*/
function isValuePresent(value) {
Expand All @@ -95,11 +95,14 @@ function isValuePresent(value) {
* Prepares a complex default value by removing newlines.
*
* @param {object} param0
* @param {string} [param0.defaultValue] - The default value.
* @param {FieldData<DirectivePropertyData>['default']} [param0.defaultValue] - The default value.
* @returns {string} - The prepared default value.
*/
function prepareComplexDefaultValue({ defaultValue = '' }) {
return defaultValue.trim().replace(/\n/g, ' ');
if (typeof defaultValue === 'string') {
return defaultValue.trim().replace(/\n/g, ' ');
}
return '';
}

module.exports = {
Expand Down
48 changes: 45 additions & 3 deletions reverse_engineering/mappers/field.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @import {FieldDefinitionNode, TypeNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldTypeProperties, PreProcessedFieldData} from "./../../shared/types/types"
* @import {FieldDefinitionNode, TypeNode, InputValueDefinitionNode, ValueNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldTypeProperties, InputTypeFieldProperties, PreProcessedFieldData} from "./../../shared/types/types"
*/

const { mapDirectivesUsage } = require('./directiveUsage');
Expand All @@ -11,7 +11,7 @@ const { BUILT_IN_SCALAR_LIST } = require('../constants/types');
* Maps a field
*
* @param {object} params
* @param {FieldDefinitionNode} params.field - The field to map
* @param {FieldDefinitionNode | InputValueDefinitionNode} params.field - The field to map
* @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
* @returns {PreProcessedFieldData} The mapped field
*/
Expand All @@ -24,6 +24,11 @@ function mapField({ field, definitionCategoryByNameMap }) {
};
const description = field.description?.value;

// Add default value handling for InputValueDefinitionNode
if ('defaultValue' in field && field.defaultValue) {
sharedProperties.default = parseDefaultValue(field.defaultValue);
}

if ('$ref' in fieldTypeProperties) {
return {
...sharedProperties,
Expand All @@ -38,6 +43,43 @@ function mapField({ field, definitionCategoryByNameMap }) {
};
}

/**
* Parses a default value from a ValueNode into a string representation
*
* @param {ValueNode} defaultValue - The default value node to parse
* @param {boolean} [isNested] - Whether this value is nested inside an object or list. Default is `false`
* @returns {InputTypeFieldProperties['default']} String representation of the default value
*/
function parseDefaultValue(defaultValue, isNested = false) {
switch (defaultValue.kind) {
case astNodeKind.INT:
return parseInt(defaultValue.value);
case astNodeKind.FLOAT:
return parseFloat(defaultValue.value);
case astNodeKind.ENUM:
return defaultValue.value;
case astNodeKind.STRING:
// Add quotes only if the string is nested in an object or list
return isNested ? `"${defaultValue.value}"` : defaultValue.value;
case astNodeKind.BOOLEAN:
return defaultValue.value.toString();
case astNodeKind.NULL:
return 'null';
case astNodeKind.LIST: {
const listValues = defaultValue.values.map(value => parseDefaultValue(value, true));
return `[${listValues.join(', ')}]`;
}
case astNodeKind.OBJECT: {
const objectFields = defaultValue.fields.map(
field => `${field.name.value}: ${parseDefaultValue(field.value, true)}`,
);
return `{ ${objectFields.join(', ')} }`;
}
default:
return '';
}
}

/**
* Recursively maps the type properties unwrapping non-null and list types and resolving named types to references
*
Expand Down
59 changes: 59 additions & 0 deletions reverse_engineering/mappers/typeDefinitions/inputType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @import {InputObjectTypeDefinitionNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, REInputTypeDefinition, REPropertiesSchema} from "../../../shared/types/types"
*/

const { sortByName } = require('../../helpers/sortByName');
const { mapDirectivesUsage } = require('../directiveUsage');
const { mapField } = require('../field');

/**
* Maps input object type definitions
*
* @param {object} params
* @param {InputObjectTypeDefinitionNode[]} params.inputObjectTypes - The input object types
* @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
* @param {FieldsOrder} params.fieldsOrder - The fields order
* @returns {REInputTypeDefinition[]} The mapped input object type definitions
*/
function getInputObjectTypeDefinitions({ inputObjectTypes = [], definitionCategoryByNameMap, fieldsOrder }) {
return inputObjectTypes.map(inputObjectType =>
mapInputObjectType({ inputObjectType, definitionCategoryByNameMap, fieldsOrder }),
);
}

/**
* Maps a single input object type definition
*
* @param {object} params
* @param {InputObjectTypeDefinitionNode} params.inputObjectType - The input object type to map
* @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
* @param {FieldsOrder} params.fieldsOrder - The fields order
* @returns {REInputTypeDefinition} The mapped input object type definition
*/
function mapInputObjectType({ inputObjectType, definitionCategoryByNameMap, fieldsOrder }) {
const properties = inputObjectType.fields
? inputObjectType.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: 'input',
name: inputObjectType.name.value,
properties: convertedProperties,
required,
description: inputObjectType.description?.value || '',
typeDirectives: mapDirectivesUsage({ directives: [...(inputObjectType.directives || [])] }),
};
}

module.exports = {
getInputObjectTypeDefinitions,
};
31 changes: 28 additions & 3 deletions reverse_engineering/mappers/typeDefinitions/typeDefinitions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* @import {DefinitionNode} from "graphql"
* @import {REDirectiveDefinition,
* REDefinitionsSchema,
* FieldsOrder,
* RECustomScalarDefinition,
* REDefinition,
Expand All @@ -14,7 +13,9 @@
* ScalarStructureType,
* REObjectTypeDefinition,
* InterfaceStructureType,
* REInterfaceDefinition} from "../../../shared/types/types"
* REInterfaceDefinition,
* REInputTypeDefinition,
* InputStructureType} from "../../../shared/types/types"
*/

const { astNodeKind } = require('../../constants/graphqlAST');
Expand All @@ -26,6 +27,7 @@ const { getDirectiveTypeDefinitions } = require('./directive');
const { getObjectTypeDefinitions } = require('./objectType');
const { getEnumTypeDefinitions } = require('./enum');
const { getInterfaceDefinitions } = require('./interface');
const { getInputObjectTypeDefinitions } = require('./inputType');

/**
* Gets the type definitions structure
Expand Down Expand Up @@ -65,13 +67,20 @@ function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames }) {
fieldsOrder,
});

const inputTypes = getInputObjectTypeDefinitions({
inputObjectTypes: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.INPUT_OBJECT_TYPE_DEFINITION }),
definitionCategoryByNameMap,
fieldsOrder,
});

const definitions = getTypeDefinitionsStructure({
fieldsOrder,
directives,
customScalars,
enums,
objectTypes,
interfaces,
inputTypes,
});

return definitions;
Expand All @@ -87,9 +96,18 @@ function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames }) {
* @param {REEnumDefinition[]} params.enums - The enum definitions
* @param {REObjectTypeDefinition[]} params.objectTypes - The object type definitions
* @param {REInterfaceDefinition[]} params.interfaces - The interface definitions
* @param {REInputTypeDefinition[]} params.inputTypes - The input type definitions
* @returns {REModelDefinitionsSchema} The type definitions structure
*/
function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, enums, objectTypes, interfaces }) {
function getTypeDefinitionsStructure({
fieldsOrder,
directives,
customScalars,
enums,
objectTypes,
interfaces,
inputTypes,
}) {
const definitions = {
['Directives']: /** @type {DirectiveStructureType} */ (
getDefinitionCategoryStructure({
Expand Down Expand Up @@ -126,6 +144,13 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, e
properties: interfaces,
})
),
['Input objects']: /** @type {InputStructureType} */ (
getDefinitionCategoryStructure({
fieldsOrder,
subtype: 'input',
properties: inputTypes,
})
),
};

return {
Expand Down
4 changes: 3 additions & 1 deletion shared/types/fe.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export type FEEnumDefinitionsSchema = Record<string, FEEnumDefinition>;
// Object type definition
export type FEObjectLikeDefinitionsSchema = Record<string, FEObjectLikeDefinition>;

export type FEObjectLikeDefinition = ObjectLikeDefinition<ImplementsInterface, DirectivePropertyData>;
export type FEObjectLikeDefinition = ObjectLikeDefinition<DirectivePropertyData> & {
implementsInterfaces?: ImplementsInterface[];
};

export type ArgumentsResultStatement = {
argumentsStatement: string; // The formatted arguments string.
Expand Down
46 changes: 30 additions & 16 deletions shared/types/re.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
EnumDefinition,
EnumValue,
StructuredDirective,
InputFieldDefaultValue,
} from './shared';

type ContainerName = string;
Expand Down Expand Up @@ -118,20 +119,20 @@ export type REDirectiveDefinition = DirectiveDefinition<Object> & {
name: string;
};

export type REDefinitionsSchema = REDirectiveDefinitionsSchema | RECustomScalarDefinitionsSchema;

export type REDirectiveDefinitionsSchema = Record<string, REDirectiveDefinition>;
export type RECustomScalarDefinitionsSchema = Record<string, RECustomScalarDefinition>;
export type REObjectDefinitionsSchema = Record<string, REObjectTypeDefinition>;
export type REEnumDefinitionsSchema = Record<string, REEnumDefinition>;
export type REInterfaceDefinitionsSchema = Record<string, REInterfaceDefinition>;
type REDirectiveDefinitionsSchema = Record<string, REDirectiveDefinition>;
type RECustomScalarDefinitionsSchema = Record<string, RECustomScalarDefinition>;
type REObjectDefinitionsSchema = Record<string, REObjectTypeDefinition>;
type REEnumDefinitionsSchema = Record<string, REEnumDefinition>;
type REInterfaceDefinitionsSchema = Record<string, REInterfaceDefinition>;
type REInputDefinitionsSchema = Record<string, REInputTypeDefinition>;

export type REDefinition =
| RECustomScalarDefinition
| REDirectiveDefinition
| REEnumDefinition
| REObjectTypeDefinition
| REInterfaceDefinition;
| REInterfaceDefinition
| REInputTypeDefinition;

export type REModelDefinitionsSchema = {
definitions: {
Expand All @@ -148,7 +149,8 @@ export type DefinitionREStructure =
| ScalarStructureType
| EnumStructureType
| ObjectStructureType
| InterfaceStructureType;
| InterfaceStructureType
| InputStructureType;

type StructureType<T> = {
type: 'type';
Expand All @@ -168,10 +170,14 @@ export type EnumStructureType = StructureType<REEnumDefinitionsSchema> & {
subtype: 'enum';
};

export type InterfaceStructureType = StructureType<REEnumDefinitionsSchema> & {
export type InterfaceStructureType = StructureType<REInterfaceDefinitionsSchema> & {
subtype: 'interface';
};

export type InputStructureType = StructureType<REInputDefinitionsSchema> & {
subtype: 'input';
};

export type RECustomScalarDefinition = CustomScalarDefinition<StructuredDirective> & {
type: 'scalar';
name: string;
Expand All @@ -193,37 +199,45 @@ export type REImplementsInterface = {
};

export type REPropertiesSchema = Record<string, FieldData<StructuredDirective>>;
type REObjectLikeDefinition = ObjectLikeDefinition<REImplementsInterface, StructuredDirective>;
type REObjectLikeDefinition = ObjectLikeDefinition<StructuredDirective> & { name: string };

export type REObjectTypeDefinition = REObjectLikeDefinition & {
type: 'object';
name: string;
implementsInterfaces?: REImplementsInterface[];
};

export type REInterfaceDefinition = REObjectLikeDefinition & {
type: 'interface';
name: string;
implementsInterfaces?: REImplementsInterface[];
};

export type REInputTypeDefinition = REObjectLikeDefinition & {
type: 'input';
};

export type PreProcessedFieldData = FieldData<StructuredDirective> &
FieldTypeProperties & {
name: string;
};

export type InputTypeFieldProperties = {
default?: InputFieldDefaultValue;
};

export type FieldTypeProperties = RegularFieldTypeProperties | ArrayFieldTypeProperties | ReferenceFieldTypeProperties;

export type RegularFieldTypeProperties = {
type RegularFieldTypeProperties = InputTypeFieldProperties & {
type: string;
required: boolean;
};

export type ArrayFieldTypeProperties = {
type ArrayFieldTypeProperties = InputTypeFieldProperties & {
type: 'List';
items?: [FieldTypeProperties];
required: boolean;
};

export type ReferenceFieldTypeProperties = {
type ReferenceFieldTypeProperties = InputTypeFieldProperties & {
$ref: string;
required: boolean;
};
Expand Down
Loading