diff --git a/.eslint/noAmbiguousReturnTypes.js b/.eslint/noAmbiguousReturnTypes.js new file mode 100644 index 0000000..e5556f6 --- /dev/null +++ b/.eslint/noAmbiguousReturnTypes.js @@ -0,0 +1,81 @@ +/** + * @import {RuleContext, RuleContext, RuleListener, RuleModule, Node} from 'eslint'; + */ + +/** + * Checks if a comment contains not object return types + * + * @param {string} commentText - The JSDoc comment text + * @returns {boolean} True if the comment has an object return type + */ +const hasObjectReturnType = commentText => { + const regex = /\*\s+@returns?\s+\{(object|Object|any|Record<\w+,\s*(any|object)>)\b/i; + return regex.test(commentText); +}; + +/** + * Creates an ESLint rule handler to detect and report usages of 'object' as a return type in JSDoc + * + * @param {RuleContext} context - The ESLint rule context object + * @returns {RuleListener} The rule listener with handlers for JSDoc comments + */ +function create(context) { + const sourceCode = context.sourceCode; + + /** + * Processes a node to find its JSDoc comment and check for object return types + * + * @param {Node} node - The AST node to check + */ + const checkJSDocComment = node => { + const comments = sourceCode.getAllComments(); + + // Find the closest comment before the node that looks like JSDoc + const jsDocComments = comments.filter( + comment => + comment.type === 'Block' && + comment.value.startsWith('*') && + comment.loc.end.line + 1 >= node.loc.start.line && + comment.loc.end.line < node.loc.start.line + 3, + ); + + for (const comment of jsDocComments) { + if (hasObjectReturnType(comment.value)) { + context.report({ + node: comment, + messageId: 'noAmbiguousReturnTypes', + }); + } + } + }; + + return { + FunctionDeclaration: checkJSDocComment, + FunctionExpression: checkJSDocComment, + ArrowFunctionExpression: checkJSDocComment, + MethodDefinition: checkJSDocComment, + }; +} + +/** + * ESLint rule to enforce specific return types instead of generic 'object' in JSDoc + * + * @type {RuleModule} + */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: "Disallow using 'object', 'any', and 'Record' type in JSDoc @returns tag", + category: 'Best Practices', + recommended: true, + }, + fixable: null, + schema: [], + messages: { + noAmbiguousReturnTypes: + "Don't use 'object', 'any', 'Record', 'Record' as a return type, use a more specific type instead", + }, + }, + create, +}; diff --git a/buildConstants.js b/buildConstants.js index ca302d2..bba580e 100644 --- a/buildConstants.js +++ b/buildConstants.js @@ -5,6 +5,7 @@ const DEFAULT_RELEASE_FOLDER_PATH = path.resolve(__dirname, 'release'); const EXCLUDED_EXTENSIONS = ['.js', '.g4', '.interp', '.tokens']; const EXCLUDED_FILES = [ '.github', + '.eslint', '.DS_Store', '.editorconfig', '.eslintignore', diff --git a/eslint.config.js b/eslint.config.js index 359e548..5dc5fa6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,6 +4,13 @@ const eslintConfigPrettier = require('eslint-config-prettier'); const importPlugin = require('eslint-plugin-import'); const unusedImportsPlugin = require('eslint-plugin-unused-imports'); const jsdocPlugin = require('eslint-plugin-jsdoc'); +const noAmbiguousReturnTypes = require('./.eslint/noAmbiguousReturnTypes'); + +const customRulesConfig = { + rules: { + 'no-ambiguous-return-types': noAmbiguousReturnTypes, + }, +}; /** * @type {import('eslint').Linter.Config[]} @@ -16,6 +23,7 @@ module.exports = [ 'jsdoc': jsdocPlugin, 'unused-imports': unusedImportsPlugin, 'prettier': prettierPlugin, + 'custom': customRulesConfig, }, }, { @@ -34,6 +42,7 @@ module.exports = [ files: ['**/*.{js,cjs,mjs}'], rules: { ...eslintConfigPrettier.rules, + 'custom/no-ambiguous-return-types': 'error', 'jsdoc/require-jsdoc': [ 'error', { diff --git a/forward_engineering/api.js b/forward_engineering/api.js index 630e02a..be89612 100644 --- a/forward_engineering/api.js +++ b/forward_engineering/api.js @@ -1,5 +1,5 @@ /** - * @import {ContainerLevelScriptFEData, GenerateContainerLevelScriptCallback, ValidateScriptCallback, Logger} from "../shared/types/types" + * @import {ContainerLevelScriptFEData, GenerateContainerLevelScriptCallback, ValidateScriptCallback, Logger, FEDirectiveDefinitionsSchema} from "../shared/types/types" */ const validationHelper = require('./helpers/schemaValidationHelper'); @@ -41,7 +41,12 @@ module.exports = { }); const directiveStatements = getDirectives({ - directives: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'directive' }), + directives: /** @type {FEDirectiveDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'directive', + }) + ), definitionsIdToNameMap, }); @@ -55,7 +60,7 @@ module.exports = { ...rootTypeStatements, ...typeDefinitionStatements, ] - .filter(Boolean) + .filter(feStatement => feStatement !== null) .map(feStatement => formatFEStatement({ feStatement })) .join('\n\n'); @@ -82,7 +87,7 @@ module.exports = { cb(null, validationResults); } catch (e) { logger.log('error', { error: e }, 'GraphQL schema validation error'); - cb(e.message); + cb(e); } }, }; diff --git a/forward_engineering/helpers/addRequiredHelper.js b/forward_engineering/helpers/addRequiredHelper.js index 7b9d005..604adb4 100644 --- a/forward_engineering/helpers/addRequiredHelper.js +++ b/forward_engineering/helpers/addRequiredHelper.js @@ -3,10 +3,10 @@ * * @param {object} param0 * @param {string} param0.type - The type name statement. - * @param {boolean} param0.required - Indicates if the field is required. + * @param {boolean} [param0.required] - Indicates if the field is required. Default is `false` * @returns {string} - The type name with required indicator. */ -function addRequired({ type, required }) { +function addRequired({ type, required = false }) { if (required) { return `${type}!`; } diff --git a/forward_engineering/helpers/feStatementFormatHelper.js b/forward_engineering/helpers/feStatementFormatHelper.js index 644149b..68890e7 100644 --- a/forward_engineering/helpers/feStatementFormatHelper.js +++ b/forward_engineering/helpers/feStatementFormatHelper.js @@ -64,7 +64,7 @@ function formatDescription(description) { } /** - * @param {FEStatement & { isParentActivated: boolean }} params + * @param {Partial & { isParentActivated: boolean }} params * @returns {string} */ function formatNestedStatements({ @@ -73,7 +73,7 @@ function formatNestedStatements({ useNestedStatementSigns, startNestedStatementsSign, endNestedStatementsSign, - nestedStatementsSeparator, + nestedStatementsSeparator = '', comment = '', }) { if (!nestedStatements?.length) { diff --git a/forward_engineering/helpers/generateIdToNameMap.js b/forward_engineering/helpers/generateIdToNameMap.js index b6d0414..337103d 100644 --- a/forward_engineering/helpers/generateIdToNameMap.js +++ b/forward_engineering/helpers/generateIdToNameMap.js @@ -1,3 +1,7 @@ +/** + * @import {IdToNameMap} from '../../shared/types/types'; + */ + // The system names are the names of the GraphQL system types (configured in the plugin to group different kinds of types). const SYSTEM_NAMES = ['Scalars', 'Enums', 'Objects', 'Interfaces', 'Input objects', 'Unions', 'Directives']; @@ -5,9 +9,10 @@ const SYSTEM_NAMES = ['Scalars', 'Enums', 'Objects', 'Interfaces', 'Input object * Generate the ID to Name map for the given model definitions schema. * * @param {object} modelDefinitionsSchema - The model definitions object properties. - * @returns {object} - The ID to Name map + * @returns {IdToNameMap} - The ID to Name map */ const generateIdToNameMap = (modelDefinitionsSchema = {}) => { + /** @type {IdToNameMap} */ let idToNameMap = {}; Object.entries(modelDefinitionsSchema).forEach(([name, schema]) => { diff --git a/forward_engineering/helpers/schemaValidationHelper.js b/forward_engineering/helpers/schemaValidationHelper.js index dd29b2e..c92e12b 100644 --- a/forward_engineering/helpers/schemaValidationHelper.js +++ b/forward_engineering/helpers/schemaValidationHelper.js @@ -1,4 +1,5 @@ /** + * @import {GraphQLError} from "graphql" * @import {ValidationResponseItem} from "../../shared/types/types" */ @@ -16,7 +17,7 @@ function validate({ schema }) { try { builtSchema = buildSchema(schema); } catch (error) { - return [mapValidationError(error)]; + return [mapValidationError(/** @type {GraphQLError} */ (error))]; } const errors = validateSchema(builtSchema); @@ -30,9 +31,7 @@ function validate({ schema }) { /** * Maps a GraphQL validation error to a custom error format. * - * @param {object} error - The GraphQL validation error. - * @param {string} error.message - The error message. - * @param {object[]} [error.locations] - The locations of the error in the schema. + * @param {GraphQLError} error - The GraphQL validation error. * @returns {ValidationResponseItem} The mapped error object. */ function mapValidationError(error) { @@ -46,10 +45,7 @@ function mapValidationError(error) { /** * Gets the error position message from a GraphQL validation error. * - * @param {object} error - The GraphQL validation error. - * @param {object[]} [error.locations] - The locations of the error in the schema. - * @param {number} error.locations[].line - The line number of the error location. - * @param {number} error.locations[].column - The column number of the error location. + * @param {GraphQLError} error - The GraphQL validation error. * @returns {string} The error position message. */ function getErrorPositionMessage(error) { diff --git a/forward_engineering/mappers/argumentDefaultValue.js b/forward_engineering/mappers/argumentDefaultValue.js index 869a08d..63b54f2 100644 --- a/forward_engineering/mappers/argumentDefaultValue.js +++ b/forward_engineering/mappers/argumentDefaultValue.js @@ -13,13 +13,13 @@ const getArgumentDefaultValue = ({ type, defaultValue = '' }) => { return `"${defaultValue}"`; } case 'Int': { - return Number.parseInt(defaultValue); + return Number.parseInt(String(defaultValue)); } case 'Float': { - return Number.parseFloat(defaultValue); + return Number.parseFloat(String(defaultValue)); } case 'Boolean': { - return defaultValue.toLowerCase() === 'true'; + return String(defaultValue).toLowerCase() === 'true'; } default: { return defaultValue; diff --git a/forward_engineering/mappers/arguments.js b/forward_engineering/mappers/arguments.js index d664b4c..b928291 100644 --- a/forward_engineering/mappers/arguments.js +++ b/forward_engineering/mappers/arguments.js @@ -25,7 +25,8 @@ const getArgumentType = ({ graphqlArgument, idToNameMap = {} }) => { if (argumentType === 'List') { const firstListItem = graphqlArgument.listItems?.[0] || {}; - const listItemType = idToNameMap[firstListItem.type] || getCheckedType({ type: firstListItem.type }) || ''; + const listTypeKey = firstListItem.type || ''; + const listItemType = idToNameMap[listTypeKey] || getCheckedType({ type: listTypeKey }) || ''; if (!listItemType) { argumentType = EMPTY_LIST; @@ -71,7 +72,7 @@ const mapArgument = ({ graphqlArgument, idToNameMap = {} }) => { * Maps an array of arguments to a formatted string with all configured properties. * * @param {object} args - Arguments object. - * @param {Argument[]} args.graphqlArguments - The arguments to map. + * @param {Argument[]} [args.graphqlArguments] - The arguments to map. * @param {IdToNameMap} [args.idToNameMap] - The ID to name map of all available types in model. * @returns {ArgumentsResultStatement} Returns an object containing the arguments as a formatted string and a warning * comment if any. diff --git a/forward_engineering/mappers/customScalars.js b/forward_engineering/mappers/customScalars.js index 9fbf825..67cae1b 100644 --- a/forward_engineering/mappers/customScalars.js +++ b/forward_engineering/mappers/customScalars.js @@ -1,27 +1,16 @@ /** - * @import {FEStatement, DirectivePropertyData, IdToNameMap} from "../../shared/types/types" + * @import {FEStatement, IdToNameMap, FECustomScalarDefinition, FECustomScalarDefinitionsSchema} from "../../shared/types/types" */ const { joinInlineStatements } = require('../helpers/feStatementJoinHelper'); const { getDirectivesUsageStatement } = require('./directiveUsageStatements'); -/** - * @typedef {object} CustomScalar - * @property {string} description - The description of the custom scalar. - * @property {boolean} isActivated - Indicates if the custom scalar is activated. - * @property {DirectivePropertyData[]} typeDirectives - The directives of the custom scalar. - */ - -/** - * @typedef {Record} CustomScalars - */ - /** * Maps a custom scalar to an FEStatement. * * @param {object} param0 * @param {string} param0.name - The name of the custom scalar. - * @param {CustomScalar} param0.customScalar - The custom scalar object. + * @param {FECustomScalarDefinition} param0.customScalar - The custom scalar object. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @returns {FEStatement} */ @@ -43,7 +32,7 @@ function mapCustomScalar({ name, customScalar, definitionsIdToNameMap }) { * Gets the custom scalars as an array of FEStatements. * * @param {object} param0 - * @param {CustomScalars} param0.customScalars - The custom scalars object. + * @param {FECustomScalarDefinitionsSchema} param0.customScalars - The custom scalars object. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @returns {FEStatement[]} */ diff --git a/forward_engineering/mappers/directiveUsageStatements.js b/forward_engineering/mappers/directiveUsageStatements.js index e8b1467..4e830e9 100644 --- a/forward_engineering/mappers/directiveUsageStatements.js +++ b/forward_engineering/mappers/directiveUsageStatements.js @@ -9,7 +9,7 @@ const { isUUID } = require('../helpers/isUUID'); * Gets the directives usage statement by mapping directives to strings and joining them. * * @param {object} params - * @param {DirectivePropertyData[]} params.directives - Array of directive definitions + * @param {DirectivePropertyData[]} [params.directives] - Array of directive definitions * @param {IdToNameMap} [params.definitionsIdToNameMap] - The definitions id to name map. * @returns {string} - The joined directive statements */ @@ -29,11 +29,11 @@ function getDirectivesUsageStatement({ directives = [], definitionsIdToNameMap = * @returns {string} - The directive statement or empty string if invalid */ function mapDirective({ directive, definitionsIdToNameMap }) { - if (directive.directiveFormat === 'Raw') { + if ('rawDirective' in directive && directive.directiveFormat === 'Raw') { return formatRawDirective({ rawDirective: directive.rawDirective }); } - if (directive.directiveFormat === 'Structured') { + if ('directiveName' in directive && directive.directiveFormat === 'Structured') { const directiveName = getDirectiveName({ directiveName: directive.directiveName, definitionsIdToNameMap }); if (!directiveName) { return ''; @@ -53,7 +53,7 @@ function mapDirective({ directive, definitionsIdToNameMap }) { * @returns {string} - The formatted arguments string or empty string if no valid arguments */ function mapDirectiveRawArguments({ directive }) { - if (directive.argumentValueFormat === 'Raw') { + if ('argumentValueFormat' in directive && directive.argumentValueFormat === 'Raw') { const argumentsValue = directive.rawArgumentValues?.replace(/\n/g, ' ').trim() || ''; if (!argumentsValue) { return ''; @@ -75,7 +75,7 @@ function mapDirectiveRawArguments({ directive }) { * @returns {string} - The formatted directive name or empty string if invalid */ function getDirectiveName({ directiveName, definitionsIdToNameMap }) { - const resolvedDirectiveName = (definitionsIdToNameMap[directiveName] || directiveName || '').trim(); + const resolvedDirectiveName = (definitionsIdToNameMap?.[directiveName] || directiveName || '').trim(); if (typeof resolvedDirectiveName !== 'string' || resolvedDirectiveName === '' || isUUID(resolvedDirectiveName)) { return ''; } diff --git a/forward_engineering/mappers/directives.js b/forward_engineering/mappers/directives.js index 6d5d879..bca84f2 100644 --- a/forward_engineering/mappers/directives.js +++ b/forward_engineering/mappers/directives.js @@ -1,5 +1,5 @@ /** - * @import {DirectiveDefinitions, Directive, FEStatement, DirectiveLocations, IdToNameMap} from "../../shared/types/types" + * @import {FEDirectiveDefinitionsSchema, FEDirectiveDefinition, FEStatement, DirectiveLocations, IdToNameMap} from "../../shared/types/types" */ const { DIRECTIVE_LOCATIONS } = require('../constants/feScriptConstants'); @@ -40,7 +40,7 @@ const getDirectiveName = name => (name.startsWith('@') ? name : `@${name}`); * * @param {object} args - The arguments object * @param {string} args.name - The name of directive - * @param {Directive} args.directive - The directive object + * @param {FEDirectiveDefinition} args.directive - The directive object * @param {IdToNameMap} args.definitionsIdToNameMap - The ID to name map of all available types in model - needs for * arguments * @returns {FEStatement} @@ -65,8 +65,8 @@ function mapDirective({ name, directive, definitionsIdToNameMap }) { * Maps directives to an FEStatement objects. * * @param {object} args - The arguments object - * @param {DirectiveDefinitions} args.definitionsIdToNameMap - The directives schema object - * @param {object} args.directives + * @param {IdToNameMap} args.definitionsIdToNameMap - The directives schema object + * @param {FEDirectiveDefinitionsSchema} args.directives * @returns {FEStatement[]} */ function getDirectives({ definitionsIdToNameMap, directives = {} }) { diff --git a/forward_engineering/mappers/enums.js b/forward_engineering/mappers/enums.js index c960b58..1b8698e 100644 --- a/forward_engineering/mappers/enums.js +++ b/forward_engineering/mappers/enums.js @@ -1,34 +1,15 @@ /** - * @import {FEStatement, DirectivePropertyData, IdToNameMap} from "../../shared/types/types" + * @import {FEStatement, FEEnumDefinition, FEEnumDefinitionsSchema, EnumValue, IdToNameMap} from "../../shared/types/types" */ const { joinInlineStatements } = require('../helpers/feStatementJoinHelper'); const { getDirectivesUsageStatement } = require('./directiveUsageStatements'); -/** - * @typedef {object} EnumValue - * @property {string} value - The name of the enum value. - * @property {string} description - The description of the enum value. - * @property {DirectivePropertyData[]} typeDirectives - The directives of the enum value. - */ - -/** - * @typedef {object} EnumDefinition - * @property {string} description - The description of the enum. - * @property {boolean} isActivated - Indicates if the enum is activated. - * @property {DirectivePropertyData[]} typeDirectives - The directives of the enum. - * @property {EnumValue[]} enumValues - The values of the emu,. - */ - -/** - * @typedef {Record} EnumDefinitions - */ - /** * Gets the enums as an array of FEStatements. * * @param {object} param0 - * @param {EnumDefinitions} param0.enumsDefinitions - The enums object. + * @param {FEEnumDefinitionsSchema} param0.enumsDefinitions - The enums object. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @returns {FEStatement[]} */ @@ -39,11 +20,11 @@ function getEnums({ enumsDefinitions, definitionsIdToNameMap }) { } /** - * Maps a enum to an FEStatement. + * Maps an enum to an FEStatement. * * @param {object} param0 * @param {string} param0.name - The name of the enum. - * @param {EnumDefinition} param0.enumDefinition - The enum definition object. + * @param {FEEnumDefinition} param0.enumDefinition - The enum definition object. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @returns {FEStatement} */ diff --git a/forward_engineering/mappers/fieldDefaultValue.js b/forward_engineering/mappers/fieldDefaultValue.js index 233bbdb..6b1169c 100644 --- a/forward_engineering/mappers/fieldDefaultValue.js +++ b/forward_engineering/mappers/fieldDefaultValue.js @@ -14,18 +14,22 @@ function getFieldDefaultValueStatement({ field }) { return ''; } - if (field.$ref) { + if ('$ref' in field && field.$ref) { return `= ${formatRefFieldDefaultValue({ defaultValue: field.default })}`; } - return `= ${formatFieldDefaultValue({ defaultValue: field.default, fieldType: field.type })}`; + if ('type' in field) { + return `= ${formatFieldDefaultValue({ defaultValue: field.default, fieldType: field.type })}`; + } + + return ''; } /** * Formats the default value for a field. * * @param {object} param0 - * @param {unknown} param0.defaultValue - The default value. + * @param {string} [param0.defaultValue] - The default value. * @param {string} param0.fieldType - The type of the field. * @returns {string} - The formatted default value. */ @@ -80,7 +84,7 @@ function isComplexDefaultValue({ defaultValue }) { /** * Checks if a value is present (not undefined or empty). * - * @param {unknown} value - The value to check. + * @param {string} [value] - The value to check. * @returns {boolean} - True if the value is present, false otherwise. */ function isValuePresent(value) { @@ -91,10 +95,10 @@ function isValuePresent(value) { * Prepares a complex default value by removing newlines. * * @param {object} param0 - * @param {string} param0.defaultValue - The default value. + * @param {string} [param0.defaultValue] - The default value. * @returns {string} - The prepared default value. */ -function prepareComplexDefaultValue({ defaultValue }) { +function prepareComplexDefaultValue({ defaultValue = '' }) { return defaultValue.trim().replace(/\n/g, ' '); } diff --git a/forward_engineering/mappers/fields.js b/forward_engineering/mappers/fields.js index d42a45e..39a4e4e 100644 --- a/forward_engineering/mappers/fields.js +++ b/forward_engineering/mappers/fields.js @@ -1,5 +1,5 @@ /** - * @import {FEStatement, FieldData, ArrayItem, IdToNameMap} from "../../shared/types/types" + * @import {FEStatement, BaseGetFieldParams, GetFieldsParams, ArrayItems, FieldData, ArrayItem, IdToNameMap} from "../../shared/types/types" */ const { joinInlineStatements } = require('../helpers/feStatementJoinHelper'); @@ -9,22 +9,13 @@ const { getDirectivesUsageStatement } = require('./directiveUsageStatements'); const { getFieldDefaultValueStatement } = require('./fieldDefaultValue'); const { addRequired } = require('../helpers/addRequiredHelper'); -/** - * @typedef {Record} FieldsData - */ - /** * Gets the fields from the model definitions. * - * @param {object} param0 - * @param {FieldsData} param0.fields - The fields to get. - * @param {string[]} param0.requiredFields - The required fields list. - * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. - * @param {boolean} param0.addArguments - Indicates if arguments should be added. - * @param {boolean} param0.addDefaultValue - Indicates if default value should be added. + * @param {GetFieldsParams} params * @returns {FEStatement[]} - The fields. */ -function getFields({ fields = [], requiredFields = [], definitionsIdToNameMap, addArguments, addDefaultValue }) { +function getFields({ fields = {}, requiredFields = [], definitionsIdToNameMap, addArguments, addDefaultValue }) { return Object.entries(fields).map(([name, fieldData]) => mapField({ name, @@ -37,6 +28,21 @@ function getFields({ fields = [], requiredFields = [], definitionsIdToNameMap, a ); } +/** + * @param {object} params + * @param {FieldData} params.fieldData + * @returns {string} + */ +function getFieldDescription({ fieldData }) { + if ('refDescription' in fieldData && fieldData.refDescription) { + return fieldData.refDescription; + } else if ('description' in fieldData && fieldData.description) { + return fieldData.description; + } + + return ''; +} + /** * Maps a field to an FEStatement. * @@ -63,7 +69,7 @@ function mapField({ name, fieldData, required, definitionsIdToNameMap, addArgume return { statement: joinInlineStatements({ statements: [fieldTypeStatement, fieldDefaultValue, directivesStatement] }), - description: fieldData.refDescription || fieldData.description, + description: getFieldDescription({ fieldData }), isActivated: fieldData.isActivated, comment: argumentsWarningComment, }; @@ -74,32 +80,44 @@ function mapField({ name, fieldData, required, definitionsIdToNameMap, addArgume * * @param {object} param0 * @param {FieldData} param0.field - The field data object. - * @param {boolean} param0.required - Indicates if the field is required. + * @param {boolean} [param0.required] - Indicates if the field is required. * @returns {string} - The field type. */ function getFieldType({ field, required }) { - if (field.$ref) { + if ('$ref' in field && field.$ref) { const definitionName = getDefinitionNameFromReferencePath({ referencePath: field.$ref }); return addRequired({ type: definitionName, required }); } - if (field.type === 'List') { + if ('type' in field && field.type === 'List') { const arrayItem = getFieldFromArrayItems({ items: field.items }); + + // When no array items are present, return a placeholder + if (!arrayItem) { + return `[Unknown]`; + } + return addRequired({ type: `[${getFieldType({ field: arrayItem, required: arrayItem.required })}]`, required, }); } - return addRequired({ type: field.type, required }); + if ('type' in field) { + return addRequired({ type: field.type, required }); + } + + // Fallback return type for types that are not recognized + // Ideally, this should never be reached + return ''; } /** * Gets the field from array items. * * @param {object} param0 - * @param {FieldData['items']} param0.items - The array items. - * @returns {ArrayItem} - The field. + * @param {ArrayItems} [param0.items] - The array items. + * @returns {ArrayItem | undefined} - The field. */ function getFieldFromArrayItems({ items }) { if (Array.isArray(items)) { @@ -108,16 +126,51 @@ function getFieldFromArrayItems({ items }) { return items; } -// TODO: Add proper JSDoc comments for the functions that are exported +/** + * Gets the fields for an object type. + * + * @param {BaseGetFieldParams} params + * @returns {FEStatement[]} + */ +function getObjectTypeFields(params) { + return getFields({ ...params, addArguments: true, addDefaultValue: false }); +} + +/** + * Gets the fields for an interface type. + * + * @param {BaseGetFieldParams} params + * @returns {FEStatement[]} + */ +function getInterfaceTypeFields(params) { + return getFields({ ...params, addArguments: true, addDefaultValue: false }); +} + +/** + * Gets the fields for an input type. + * + * @param {BaseGetFieldParams} params + * @returns {FEStatement[]} + */ +function getInputTypeFields(params) { + return getFields({ ...params, addArguments: false, addDefaultValue: true }); +} + +/** + * Gets the fields for a root type. + * + * @param {BaseGetFieldParams} params + * @returns {FEStatement[]} + */ +function getRootTypeFields(params) { + return getFields({ ...params, addArguments: true, addDefaultValue: false }); +} + module.exports = { - // eslint-disable-next-line jsdoc/require-jsdoc - getObjectTypeFields: params => getFields({ ...params, addArguments: true, addDefaultValue: false }), - // eslint-disable-next-line jsdoc/require-jsdoc - getInterfaceTypeFields: params => getFields({ ...params, addArguments: true, addDefaultValue: false }), - // eslint-disable-next-line jsdoc/require-jsdoc - getInputTypeFields: params => getFields({ ...params, addArguments: false, addDefaultValue: true }), - // eslint-disable-next-line jsdoc/require-jsdoc - getRootTypeFields: params => getFields({ ...params, addArguments: true, addDefaultValue: false }), + getObjectTypeFields, + getInterfaceTypeFields, + getInputTypeFields, + getRootTypeFields, // exported only for tests: mapField, diff --git a/forward_engineering/mappers/implementsInterfaces.js b/forward_engineering/mappers/implementsInterfaces.js index 17d6a44..ca41929 100644 --- a/forward_engineering/mappers/implementsInterfaces.js +++ b/forward_engineering/mappers/implementsInterfaces.js @@ -6,7 +6,7 @@ * Get implements interfaces statement * * @param {object} param0 - * @param {ImplementsInterface[]} param0.interfaces - The interfaces to implement. + * @param {ImplementsInterface[]} [param0.interfaces] - The interfaces to implement. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @returns {string} - The implements interfaces statement. */ diff --git a/forward_engineering/mappers/objectLikeType.js b/forward_engineering/mappers/objectLikeType.js index 45caf88..f7473b8 100644 --- a/forward_engineering/mappers/objectLikeType.js +++ b/forward_engineering/mappers/objectLikeType.js @@ -1,5 +1,5 @@ /** - * @import {FEStatement, ObjectLikeTypeDefinition, ObjectLikeTypeDefinitions, IdToNameMap} from "../../shared/types/types" + * @import {FEStatement, FEObjectLikeDefinition, FEObjectLikeDefinitionsSchema, IdToNameMap} from "../../shared/types/types" */ const { joinInlineStatements } = require('../helpers/feStatementJoinHelper'); @@ -10,7 +10,7 @@ const { getImplementsInterfacesStatement } = require('./implementsInterfaces'); * Gets the object-like types from the model definitions. * * @param {object} param0 - * @param {ObjectLikeTypeDefinitions} param0.objectTypes - The object-like types to get. + * @param {FEObjectLikeDefinitionsSchema} param0.objectTypes - The object-like types to get. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @param {string} param0.typeKeyword - The type keyword ("type", "interface", "input"). * @param {Function} param0.getFieldsFunction - The function to get fields for the type. @@ -27,7 +27,7 @@ function getObjectLikeTypes({ objectTypes, definitionsIdToNameMap, typeKeyword, * * @param {object} param0 * @param {string} param0.name - The name of the object. - * @param {ObjectLikeTypeDefinition} param0.objectType - The object-like type definition object. + * @param {FEObjectLikeDefinition} param0.objectType - The object-like type definition object. * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map. * @param {string} param0.typeKeyword - The type keyword ("type", "interface", "input"). * @param {Function} param0.getFieldsFunction - The function to get fields for the type. diff --git a/forward_engineering/mappers/rootTypes.js b/forward_engineering/mappers/rootTypes.js index 70a380f..29da077 100644 --- a/forward_engineering/mappers/rootTypes.js +++ b/forward_engineering/mappers/rootTypes.js @@ -33,7 +33,7 @@ function getRootSchemaStatement({ rootTypeNames, rootTypeStatements, containerPr acc.push({ statement: `${key}: ${rootTypeNames[key]}` }); } return acc; - }, []); + }, /** @type {FEStatement[]} */ ([])); if (nestedStatements.length === 0) { return null; @@ -56,7 +56,7 @@ function getRootSchemaStatement({ rootTypeNames, rootTypeStatements, containerPr * container properties. * * @param {object} param0 - * @param {ContainerDetails | undefined} param0.containerProperties - The container properties. + * @param {ContainerDetails} [param0.containerProperties] - The container properties. * @returns {RootTypeNamesParameter} - The root type names. */ function getRootTypeNames({ containerProperties }) { @@ -129,7 +129,7 @@ function getRootTypes({ entityIdToJsonSchemaMap, entityIdToPropertiesMap, rootTy }), ]; - return rootTypes.filter(Boolean); + return rootTypes.filter(feStatement => feStatement !== null); } /** @@ -158,6 +158,7 @@ function getRootType({ Object.entries(entityIdToJsonSchemaMap).forEach(([entityId, entityJson]) => { const entityProperties = entityIdToPropertiesMap[entityId]?.[0]; + const entityOperationType = entityProperties?.operationType; if (entityOperationType !== rootType) { return; diff --git a/forward_engineering/mappers/typeDefinitions.js b/forward_engineering/mappers/typeDefinitions.js index 4c90480..50a6839 100644 --- a/forward_engineering/mappers/typeDefinitions.js +++ b/forward_engineering/mappers/typeDefinitions.js @@ -1,5 +1,5 @@ /** - * @import {IdToNameMap, FEStatement} from "../../shared/types/types" + * @import {IdToNameMap, FEStatement, FEDefinitionsSchema, FEEnumDefinitionsSchema, FECustomScalarDefinitionsSchema, FEUnionDefinitionsSchema, FEObjectLikeDefinitionsSchema} from "../../shared/types/types" */ const { getCustomScalars } = require('./customScalars'); @@ -18,32 +18,64 @@ const { getObjectTypeFields, getInterfaceTypeFields, getInputTypeFields } = requ */ function getTypeDefinitionStatements({ modelDefinitions, definitionsIdToNameMap }) { const customScalars = getCustomScalars({ - customScalars: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'scalar' }), + customScalars: /** @type {FECustomScalarDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'scalar', + }) + ), definitionsIdToNameMap, }); const enums = getEnums({ - enumsDefinitions: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'enum' }), + enumsDefinitions: /** @type {FEEnumDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'enum', + }) + ), definitionsIdToNameMap, }); const objectTypes = getObjectLikeTypes({ - objectTypes: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'object' }), + objectTypes: /** @type {FEObjectLikeDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'object', + }) + ), definitionsIdToNameMap, typeKeyword: 'type', getFieldsFunction: getObjectTypeFields, }); const interfaceTypes = getObjectLikeTypes({ - objectTypes: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'interface' }), + objectTypes: /** @type {FEObjectLikeDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'interface', + }) + ), definitionsIdToNameMap, typeKeyword: 'interface', getFieldsFunction: getInterfaceTypeFields, }); const inputTypes = getObjectLikeTypes({ - objectTypes: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'input' }), + objectTypes: /** @type {FEObjectLikeDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'input', + }) + ), definitionsIdToNameMap, typeKeyword: 'input', getFieldsFunction: getInputTypeFields, }); - const unions = getUnions({ unions: getModelDefinitionsBySubtype({ modelDefinitions, subtype: 'union' }) }); + const unions = getUnions({ + unions: /** @type {FEUnionDefinitionsSchema} */ ( + getModelDefinitionsBySubtype({ + modelDefinitions, + subtype: 'union', + }) + ), + }); return [...customScalars, ...enums, ...inputTypes, ...interfaceTypes, ...objectTypes, ...unions]; } @@ -54,7 +86,7 @@ function getTypeDefinitionStatements({ modelDefinitions, definitionsIdToNameMap * @param {object} param0 - The parameter object. * @param {object} param0.modelDefinitions - The model definitions object. * @param {string} param0.subtype - The subtype to filter by. - * @returns {object} - The model definitions found by parent's subtype. + * @returns {FEDefinitionsSchema} - The model definitions found by parent's subtype. */ function getModelDefinitionsBySubtype({ modelDefinitions, subtype }) { const subtypeDefinitions = Object.values(modelDefinitions.properties).find( diff --git a/forward_engineering/mappers/unions.js b/forward_engineering/mappers/unions.js index 2d78fa7..c18702a 100644 --- a/forward_engineering/mappers/unions.js +++ b/forward_engineering/mappers/unions.js @@ -1,5 +1,5 @@ /** - * @import {UnionDefinitions, FEStatement, Union, UnionMemberType} from "../../shared/types/types" + * @import {FEUnionDefinitionsSchema, FEStatement, FEUnionDefinition, UnionMemberType} from "../../shared/types/types" */ const { getDefinitionNameFromReferencePath } = require('../helpers/referenceHelper'); @@ -29,7 +29,7 @@ const getUnionMemberTypes = ({ unionMemberTypes }) => { * * @param {object} args - The arguments * @param {string} args.name - The name of the union. - * @param {Union} args.union - The union object with all properties + * @param {FEUnionDefinition} args.union - The union object with all properties * @returns {FEStatement} */ const mapUnion = ({ name, union }) => { @@ -45,7 +45,7 @@ const mapUnion = ({ name, union }) => { * Maps the union types to an array of FEStatement. * * @param {object} args - The arguments - * @param {UnionDefinitions} args.unions - The union types schema. + * @param {FEUnionDefinitionsSchema} args.unions - The union types schema. * @returns {FEStatement[]} */ const getUnions = ({ unions }) => { diff --git a/package.json b/package.json index d112459..f8d23a0 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ }, "simple-git-hooks": { "pre-commit": "npx lint-staged", - "pre-push": "npx eslint . && npm run test:unit" + "pre-push": "npm run lint && npm run check:types && npm run test:unit" }, "scripts": { "lint": "eslint . --max-warnings=0", diff --git a/reverse_engineering/constants/properties.js b/reverse_engineering/constants/properties.js index fe4107a..9fba555 100644 --- a/reverse_engineering/constants/properties.js +++ b/reverse_engineering/constants/properties.js @@ -1,7 +1,16 @@ +/** + * @type {{ structured: 'Structured'; raw: 'Raw' }} + * @readonly + */ const DIRECTIVE_FORMAT = { structured: 'Structured', + raw: 'Raw', }; +/** + * @type {{ raw: 'Raw' }} + * @readonly + */ const ARGUMENT_VALUE_FORMAT = { raw: 'Raw', }; diff --git a/reverse_engineering/helpers/sortByName.js b/reverse_engineering/helpers/sortByName.js index 1abe67b..0402b1e 100644 --- a/reverse_engineering/helpers/sortByName.js +++ b/reverse_engineering/helpers/sortByName.js @@ -1,14 +1,14 @@ /** - * @import {FieldsOrder} from "../../shared/types/types" + * @import {FieldsOrder, REDefinition} from "../../shared/types/types" */ /** * Sorts an array of objects by the name according to the fields order option * * @param {object} params - * @param {object[]} params.items - The items to sort + * @param {REDefinition[]} params.items - The items to sort * @param {FieldsOrder} params.fieldsOrder - The fields order - * @returns {object[]} The sorted items + * @returns {REDefinition[]} The sorted items */ function sortByName({ items, fieldsOrder }) { if (!Array.isArray(items)) { diff --git a/reverse_engineering/mappers/directiveUsage.js b/reverse_engineering/mappers/directiveUsage.js index 180d7d0..0ef1f98 100644 --- a/reverse_engineering/mappers/directiveUsage.js +++ b/reverse_engineering/mappers/directiveUsage.js @@ -1,6 +1,6 @@ /** * @import {DirectiveNode, ArgumentNode, ValueNode} from "graphql" - * @import {DirectiveUsage} from "../../shared/types/types" + * @import {StructuredDirective} from "../../shared/types/types" */ const { astNodeKind } = require('../constants/graphqlAST'); @@ -12,7 +12,7 @@ const { getDirectiveName } = require('./directiveName'); * * @param {object} params * @param {DirectiveNode[]} [params.directives] - The directives - * @returns {DirectiveUsage[]} The mapped directives usage + * @returns {StructuredDirective[]} The mapped directives usage */ function mapDirectivesUsage({ directives = [] }) { return directives.map(directive => { diff --git a/reverse_engineering/mappers/typeDefinitions/customScalar.js b/reverse_engineering/mappers/typeDefinitions/customScalar.js index 5e321e6..5cf4ca9 100644 --- a/reverse_engineering/mappers/typeDefinitions/customScalar.js +++ b/reverse_engineering/mappers/typeDefinitions/customScalar.js @@ -1,6 +1,6 @@ /** * @import {ScalarTypeDefinitionNode} from "graphql" - * @import {CustomScalarDefinition} from "../../../shared/types/types" + * @import {RECustomScalarDefinition} from "../../../shared/types/types" */ const { mapDirectivesUsage } = require('../directiveUsage'); @@ -10,7 +10,7 @@ const { mapDirectivesUsage } = require('../directiveUsage'); * * @param {object} params * @param {ScalarTypeDefinitionNode[]} params.customScalars - The custom scalars - * @returns {CustomScalarDefinition[]} The mapped custom scalar type definitions + * @returns {RECustomScalarDefinition[]} The mapped custom scalar type definitions */ function getCustomScalarTypeDefinitions({ customScalars = [] }) { return customScalars.map(scalar => mapCustomScalar({ scalar })); @@ -21,7 +21,7 @@ function getCustomScalarTypeDefinitions({ customScalars = [] }) { * * @param {object} params * @param {ScalarTypeDefinitionNode} params.scalar - The scalar to map - * @returns {CustomScalarDefinition} The mapped custom scalar definition + * @returns {RECustomScalarDefinition} The mapped custom scalar definition */ function mapCustomScalar({ scalar }) { return { diff --git a/reverse_engineering/mappers/typeDefinitions/directive.js b/reverse_engineering/mappers/typeDefinitions/directive.js index 2c8c618..0105395 100644 --- a/reverse_engineering/mappers/typeDefinitions/directive.js +++ b/reverse_engineering/mappers/typeDefinitions/directive.js @@ -1,6 +1,6 @@ /** * @import {DirectiveDefinitionNode} from "graphql" - * @import {DirectiveDefinition} from "../../../shared/types/types" + * @import {REDirectiveDefinition, DirectiveLocations} from "../../../shared/types/types" */ const { getDirectiveName } = require('../directiveName'); @@ -28,7 +28,7 @@ const locationMap = { * * @param {object} params * @param {DirectiveDefinitionNode[]} params.directives - The directives - * @returns {DirectiveDefinition[]} The mapped directive type definitions + * @returns {REDirectiveDefinition[]} The mapped directive type definitions */ function getDirectiveTypeDefinitions({ directives = [] }) { return directives.map(directive => mapDirective({ directive })); @@ -39,16 +39,19 @@ function getDirectiveTypeDefinitions({ directives = [] }) { * * @param {object} params * @param {DirectiveDefinitionNode} params.directive - The directive to map - * @returns {DirectiveDefinition} The mapped directive definition + * @returns {REDirectiveDefinition} The mapped directive definition */ function mapDirective({ directive }) { - const locations = directive.locations.reduce((acc, location) => { - const locationKey = locationMap[location.value]; - if (locationKey) { - acc[locationKey] = true; - } - return acc; - }, {}); + const locations = directive.locations.reduce( + (acc, location) => { + const locationKey = locationMap[location.value]; + if (locationKey) { + acc[locationKey] = true; + } + return acc; + }, + /** @type {DirectiveLocations} */ {}, + ); return { type: 'directive', diff --git a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js index 46c280f..712ed26 100644 --- a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js +++ b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js @@ -1,5 +1,6 @@ /** - * @import {DirectiveDefinition, FieldsOrder, CustomScalarDefinition} from "../../../shared/types/types" + * @import {DefinitionNode} from "graphql" + * @import {REDirectiveDefinition, REDefinitionsSchema, FieldsOrder, RECustomScalarDefinition, REDefinition, REModelDefinitionsSchema, DefinitionREStructure, DirectiveStructureType, ScalarStructureType} from "../../../shared/types/types" */ const { astNodeKind } = require('../../constants/graphqlAST'); @@ -12,9 +13,9 @@ const { getDirectiveTypeDefinitions } = require('./directive'); * Gets the type definitions structure * * @param {object} params - * @param {object[]} params.typeDefinitions - The type definitions nodes + * @param {DefinitionNode[]} params.typeDefinitions - The type definitions nodes * @param {FieldsOrder} params.fieldsOrder - The fields order - * @returns {object} The mapped type definitions + * @returns {REModelDefinitionsSchema} The mapped type definitions */ function getTypeDefinitions({ typeDefinitions, fieldsOrder }) { const directives = getDirectiveTypeDefinitions({ @@ -34,22 +35,26 @@ function getTypeDefinitions({ typeDefinitions, fieldsOrder }) { * * @param {object} params * @param {FieldsOrder} params.fieldsOrder - The fields order - * @param {DirectiveDefinition[]} params.directives - The directive definitions - * @param {CustomScalarDefinition[]} params.customScalars - The custom scalar definitions - * @returns {object} The type definitions structure + * @param {REDirectiveDefinition[]} params.directives - The directive definitions + * @param {RECustomScalarDefinition[]} params.customScalars - The custom scalar definitions + * @returns {REModelDefinitionsSchema} The type definitions structure */ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars }) { const definitions = { - ['Directives']: getDefinitionCategoryStructure({ - fieldsOrder, - subtype: 'directive', - properties: directives, - }), - ['Scalars']: getDefinitionCategoryStructure({ - fieldsOrder, - subtype: 'scalar', - properties: customScalars, - }), + ['Directives']: /** @type {DirectiveStructureType} */ ( + getDefinitionCategoryStructure({ + fieldsOrder, + subtype: 'directive', + properties: directives, + }) + ), + ['Scalars']: /** @type {ScalarStructureType} */ ( + getDefinitionCategoryStructure({ + fieldsOrder, + subtype: 'scalar', + properties: customScalars, + }) + ), }; return { @@ -62,9 +67,9 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars }) * * @param {object} params * @param {FieldsOrder} params.fieldsOrder - The fields order - * @param {string} params.subtype - The subtype of the definition - * @param {object[]} params.properties - The properties to structure - * @returns {object} The definition category structure + * @param {DefinitionREStructure['subtype']} params.subtype - The subtype of the definition + * @param {REDefinition[]} params.properties - The properties to structure + * @returns {DefinitionREStructure} The definition category structure */ function getDefinitionCategoryStructure({ fieldsOrder, subtype, properties }) { const sortedFields = sortByName({ items: properties, fieldsOrder }); @@ -73,10 +78,13 @@ function getDefinitionCategoryStructure({ fieldsOrder, subtype, properties }) { type: 'type', subtype, structureType: true, - properties: sortedFields.reduce((acc, prop) => { - acc[prop.name] = prop; - return acc; - }, {}), + properties: sortedFields.reduce( + (acc, prop) => { + acc[prop.name] = prop; + return acc; + }, + /** @type {REDefinitionsSchema} */ {}, + ), }; } diff --git a/shared/types/fe.d.ts b/shared/types/fe.d.ts index 696f183..3a070c6 100644 --- a/shared/types/fe.d.ts +++ b/shared/types/fe.d.ts @@ -1,4 +1,10 @@ -import { DirectiveLocations } from './shared'; +import { + ContainerSchemaRootTypes, + CustomScalarDefinition, + DirectiveDefinition, + DirectiveLocations, + DirectivePropertyData, +} from './shared'; export type FEStatement = { statement: string; @@ -14,28 +20,60 @@ export type FEStatement = { export type IdToNameMap = Record; +export type FEDefinitionsSchema = FEObjectLikeDefinitionsSchema + | FECustomScalarDefinitionsSchema + | FEEnumDefinitionsSchema + | FEUnionDefinitionsSchema + | FEDirectiveDefinitionsSchema; + +// Custom scalars +export type FECustomScalarDefinitionsSchema = Record; + +export type FECustomScalarDefinition = CustomScalarDefinition; + +// Enum +export type EnumValue = { + value: string; // The name of the enum value + description?: string; // The description of the enum value + typeDirectives?: DirectivePropertyData[]; // The directives of the enum value +}; + +export type FEEnumDefinition = { + description?: string; // The description of the enum + isActivated?: boolean; // Indicates if the enum is activated + typeDirectives?: DirectivePropertyData[]; // The directives of the enum + enumValues: EnumValue[]; // The values of the enum +}; + +export type FEEnumDefinitionsSchema = Record; + // Object type definition -export type ObjectLikeTypeDefinitions = Record; +export type FEObjectLikeDefinitionsSchema = Record; -export type ObjectLikeTypeDefinition = { +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?: ArrayItem | ArrayItem[]; // Items of the List type + items?: ArrayItems; // Items of the List type arguments?: Argument[]; // Arguments of the field - default?: unknown; // Default value of the field + default?: string; // Default value of the field }; type ReferenceFieldData = { @@ -44,7 +82,7 @@ type ReferenceFieldData = { refDescription?: string; // Description of the reference fieldDirectives?: DirectivePropertyData[]; // Directives for the field arguments?: Argument[]; // Arguments of the field - default?: unknown; // Default value of the reference + default?: string; // Default value of the reference }; export type ArrayItem = FieldData & { @@ -78,34 +116,15 @@ export type FEDirectiveLocations = DirectiveLocations & { GUID: string; }; -export type Directive = { +export type FEDirectiveDefinition = DirectiveDefinition & { GUID: string; - type: 'directive'; - description?: string; additionalProperties?: boolean; - comments?: string; ignore_z_value: boolean; isActivated?: boolean; schemaType: string; - directiveLocations?: FEDirectiveLocations; - arguments?: Argument[]; }; -export type DirectiveDefinitions = Record; - -export type DirectivePropertyData = { - directiveFormat: 'Structured' | 'Raw'; // Format of the directive -} & (StructuredDirective | RawDirective); - -type RawDirective = { - rawDirective: string; // Raw directive string -}; - -type StructuredDirective = { - directiveName: string; // Name of a built-in directive or GUID of a custom directive - argumentValueFormat: 'Raw'; // Format of the argument values - rawArgumentValues: string; // Raw argument values -}; +export type FEDirectiveDefinitionsSchema = Record; export type ImplementsInterface = { interface: string; // ID of the interface @@ -125,7 +144,7 @@ type OneOfMeta = { isActivated: boolean; }; -export type Union = { +export type FEUnionDefinition = { type: 'union'; GUID: string; description?: string; @@ -140,7 +159,7 @@ export type Union = { snippet: 'union'; }; -export type UnionDefinitions = Record; +export type FEUnionDefinitionsSchema = Record; // Root types export type RootTypeNamesParameter = { @@ -149,8 +168,13 @@ export type RootTypeNamesParameter = { subscription: string; }; +type EntityDetails = { + operationType?: string + typeDirectives?: DirectivePropertyData[]; +} + export type EntityIdToJsonSchemaMap = Record; -export type EntityIdToPropertiesMap = Record; +export type EntityIdToPropertiesMap = Record; // API parameters @@ -181,7 +205,7 @@ export type ContainerDetails = { isActivated: boolean; comments?: string; businessName?: string; - schemaRootTypes: RootTypeNamesParameter; + schemaRootTypes: ContainerSchemaRootTypes; graphDirectives: DirectivePropertyData[]; }; @@ -199,7 +223,7 @@ export type ContainerLevelScriptFEData = { }; }; -export type GenerateContainerLevelScriptCallback = (error: Error | null, script?: string) => void; +export type GenerateContainerLevelScriptCallback = (error: Error | null | unknown, script?: string) => void; export type ValidationResponseItem = { type: string; // The type of the entity (e.g., 'error', 'success'). @@ -208,4 +232,15 @@ export type ValidationResponseItem = { context?: string; // The context of the entity, typically additional information. }; -export type ValidateScriptCallback = (error: Error | null, validationErrors?: ValidationResponseItem[]) => void; +export type ValidateScriptCallback = (error: Error | null | unknown, validationErrors?: ValidationResponseItem[]) => void; + +export type BaseGetFieldParams = { + fields?: FieldSchema; // The fields to get + requiredFields?: string[]; // The required fields list + definitionsIdToNameMap: IdToNameMap; // The definitions id to name map +} + +export type GetFieldsParams = BaseGetFieldParams & { + addArguments: boolean; // Indicates if arguments should be added. + addDefaultValue: boolean; // Indicates if default value should be added. +} diff --git a/shared/types/re.d.ts b/shared/types/re.d.ts index 010884d..8639017 100644 --- a/shared/types/re.d.ts +++ b/shared/types/re.d.ts @@ -1,4 +1,9 @@ -import { DirectiveLocations } from './shared'; +import { + ContainerSchemaRootTypes, + CustomScalarDefinition, + DirectiveDefinition, + StructuredDirective, +} from './shared'; type ContainerName = string; @@ -6,13 +11,7 @@ export type ContainerInfo = { name: ContainerName; description?: string; // container description schemaRootTypes?: ContainerSchemaRootTypes; // container schema root types - graphDirectives?: DirectiveUsage[]; // container graph directives -}; - -export type ContainerSchemaRootTypes = { - rootQuery?: string; // root query name - rootMutation?: string; // root mutation name - rootSubscription?: string; // root subscription name + graphDirectives?: StructuredDirective[]; // container graph directives }; export type FileREEntityResponseData = { @@ -111,26 +110,43 @@ export type REConnectionInfo = GeneralRESettings & { connectionSettings: ConnectionSettings; }; -export type DirectiveUsage = { - directiveFormat: string; - directiveName: string; - argumentValueFormat: string; - rawArgumentValues: string; -}; - -export type DirectiveDefinition = { - type: 'directive'; +export type REDirectiveDefinition = DirectiveDefinition & { name: string; - description?: string; - arguments?: Object[]; // TODO: update when arguments are ready - directiveLocations: DirectiveLocations; }; -export type CustomScalarDefinition = { +export type REDefinitionsSchema = REDirectiveDefinitionsSchema | RECustomScalarDefinitionsSchema; + +export type REDirectiveDefinitionsSchema = Record; +export type RECustomScalarDefinitionsSchema = Record; + +export type REDefinition = RECustomScalarDefinition | REDirectiveDefinition; + +export type REModelDefinitionsSchema = { + definitions: { + Directives: DirectiveStructureType; + Scalars: ScalarStructureType; + } +} + +export type DefinitionREStructure = DirectiveStructureType | ScalarStructureType; + +type StructureType = { + type: 'type'; + structureType: true, + properties: T +} + +export type DirectiveStructureType = StructureType & { + subtype: 'directive'; +} + +export type ScalarStructureType = StructureType & { + subtype: 'scalar'; +} + +export type RECustomScalarDefinition = CustomScalarDefinition & { type: 'scalar'; name: string; - description?: string; - typeDirectives?: DirectiveUsage[]; } export type TestConnectionCallback = (err?: Error | unknown) => void; diff --git a/shared/types/shared.d.ts b/shared/types/shared.d.ts index bf1cf33..bc3ea35 100644 --- a/shared/types/shared.d.ts +++ b/shared/types/shared.d.ts @@ -16,6 +16,12 @@ export type Logger = { progress: (data: object) => void; }; +export type ContainerSchemaRootTypes = { + rootQuery?: string; // root query name + rootMutation?: string; // root mutation name + rootSubscription?: string; // root subscription name +}; + export type DirectiveLocations = { argumentDefinition?: boolean; enum?: boolean; @@ -33,3 +39,31 @@ export type DirectiveLocations = { subscription?: boolean; union?: boolean; }; + +export type DirectivePropertyData = StructuredDirective | RawDirective; + +type RawDirective = { + directiveFormat: 'Raw'; // Format of the directive + rawDirective: string; // Raw directive string +}; + +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 + rawArgumentValues: string; // Raw argument values +}; + +export type CustomScalarDefinition = { + description?: string; + isActivated?: boolean; + typeDirectives?: T[] +} + +export type DirectiveDefinition = { + type: 'directive'; + description?: string; + comments?: string; + arguments?: T[]; + directiveLocations: D; +}