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
30 changes: 29 additions & 1 deletion reverse_engineering/mappers/field.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
/**
* @import {FieldDefinitionNode, TypeNode, InputValueDefinitionNode, ValueNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldTypeProperties, InputTypeFieldProperties, PreProcessedFieldData} from "./../../shared/types/types"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, FieldTypeProperties, InputTypeFieldProperties, PreProcessedFieldData, REFieldsSchemaProperties, REPropertiesSchema} from "./../../shared/types/types"
*/

const { mapDirectivesUsage } = require('./directiveUsage');
const { astNodeKind } = require('../constants/graphqlAST');
const { BUILT_IN_SCALAR_LIST } = require('../constants/types');
const { sortByName } = require('../helpers/sortByName');

/**
* Maps a list of fields to a schema
*
* @param {object} params
* @param {FieldDefinitionNode[] | InputValueDefinitionNode[]} params.fields - The fields to map
* @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
* @param {FieldsOrder} params.fieldsOrder - The fields order
* @returns {REFieldsSchemaProperties} The mapped schema
*/
function getFieldsSchema({ fields, definitionCategoryByNameMap, fieldsOrder }) {
const properties = fields ? 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 {
properties: convertedProperties,
required,
};
}

/**
* Maps a field
Expand Down Expand Up @@ -147,4 +174,5 @@ function isBuiltInScalar({ typeName }) {

module.exports = {
mapField,
getFieldsSchema,
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const { mapDirectivesUsage } = require('./directiveUsage');
* @param {string} params.graphName - The name of the graph
* @returns {ContainerInfo} The mapped container
*/
function mapRootSchemaTypesToContainer({ rootSchemaNode, graphName = 'New Graph' }) {
function mapRootSchemaToContainer({ rootSchemaNode, graphName = 'New Graph' }) {
if (!rootSchemaNode) {
return { name: graphName };
}
Expand Down Expand Up @@ -56,5 +56,5 @@ function mapSchemaRootTypes({ schemaRootTypes }) {
}

module.exports = {
mapRootSchemaTypesToContainer,
mapRootSchemaToContainer,
};
68 changes: 68 additions & 0 deletions reverse_engineering/mappers/rootTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @import {ObjectTypeDefinitionNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, RootTypeEntity} from "../../shared/types/types"
*/

const { mapDirectivesUsage } = require('./directiveUsage');
const { getFieldsSchema } = require('./field');

/**
* Maps the schema root types to entities
*
* @param {object} params
* @param {ObjectTypeDefinitionNode[]} params.rootTypeNodes - The schema root type nodes
* @param {string[]} params.schemaRootTypesMap - The schema root types names map
* @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
* @param {FieldsOrder} params.fieldsOrder - The fields order
* @returns {RootTypeEntity[]} The mapped container
*/
function mapRootTypesToEntities({ rootTypeNodes, schemaRootTypesMap, definitionCategoryByNameMap, fieldsOrder }) {
if (!rootTypeNodes) {
return [];
}

return rootTypeNodes.map(rootTypeNode => {
const { properties, required } = getFieldsSchema({
fields: [...(rootTypeNode.fields || [])],
definitionCategoryByNameMap,
fieldsOrder,
});
return {
name: rootTypeNode.name.value,
data: {
type: 'object',
description: rootTypeNode.description?.value || '',
operationType: getOperationType({ typeName: rootTypeNode.name.value, schemaRootTypesMap }),
typeDirectives: mapDirectivesUsage({ directives: [...(rootTypeNode.directives || [])] }),
properties,
required,
},
};
});
}

/**
* Maps the operation type
*
* @param {object} params
* @param {string} params.typeName - The type name
* @param {string[]} params.schemaRootTypesMap - The schema root types names map
* @returns {string} The operation type
*/
function getOperationType({ typeName, schemaRootTypesMap }) {
const [queryType, mutationType, subscriptionType] = schemaRootTypesMap;
if (typeName === queryType) {
return 'Query';
}
if (typeName === mutationType) {
return 'Mutation';
}
if (typeName === subscriptionType) {
return 'Subscription';
}
return '';
}

module.exports = {
mapRootTypesToEntities,
};
55 changes: 36 additions & 19 deletions reverse_engineering/mappers/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
* @import {Logger, FileREEntityResponseData, FieldsOrder} from "../../shared/types/types"
*/

const { Kind } = require('graphql');
const { mapRootSchemaTypesToContainer } = require('./rootSchemaTypes');
const { mapRootSchemaToContainer } = require('./rootSchema');
const { findNodesByKind } = require('../helpers/findNodesByKind');
const { getTypeDefinitions } = require('./typeDefinitions/typeDefinitions');
const { mapRootTypesToEntities } = require('./rootTypes');
const { astNodeKind } = require('../constants/graphqlAST');
const { getDefinitionCategoryByNameMap } = require('../helpers/getDefinitionCategoryByNameMap');

/**
* Maps a GraphQL schema to a RE response
Expand All @@ -23,8 +25,8 @@ function getMappedSchema({ schemaItems, graphName, logger, fieldsOrder }) {
if (!schemaItems) {
throw new Error('Schema items are empty');
}
const container = mapRootSchemaTypesToContainer({
rootSchemaNode: findNodesByKind({ nodes: schemaItems, kind: Kind.SCHEMA_DEFINITION })[0],
const container = mapRootSchemaToContainer({
rootSchemaNode: findNodesByKind({ nodes: schemaItems, kind: astNodeKind.SCHEMA_DEFINITION })[0],
graphName,
});
const rootTypeNames = [
Expand All @@ -33,23 +35,38 @@ function getMappedSchema({ schemaItems, graphName, logger, fieldsOrder }) {
container.schemaRootTypes?.rootSubscription || 'Subscription',
];

const typeDefinitions = getTypeDefinitions({ typeDefinitions: schemaItems, fieldsOrder, rootTypeNames });
const definitionCategoryByNameMap = getDefinitionCategoryByNameMap({ nodes: schemaItems });

return [
// TODO: remove test collection
{
jsonSchema: '{ "type": "object", "operationType": "Query" }',
objectNames: {
collectionName: 'Test Collection',
},
doc: {
bucketInfo: container,
collectionName: 'Test Collection',
dbName: container.name,
modelDefinitions: JSON.stringify(typeDefinitions),
},
const rootTypeNodes = findNodesByKind({
nodes: schemaItems,
kind: astNodeKind.OBJECT_TYPE_DEFINITION,
}).filter(node => rootTypeNames.includes(node.name.value));
const entities = mapRootTypesToEntities({
rootTypeNodes,
definitionCategoryByNameMap,
fieldsOrder,
schemaRootTypesMap: rootTypeNames,
});

const typeDefinitions = getTypeDefinitions({
typeDefinitions: schemaItems,
fieldsOrder,
rootTypeNames,
definitionCategoryByNameMap,
});

return entities.map(entity => ({
jsonSchema: JSON.stringify(entity.data),
objectNames: {
collectionName: entity.name,
},
];
doc: {
bucketInfo: container,
collectionName: entity.name,
dbName: container.name,
modelDefinitions: JSON.stringify(typeDefinitions),
},
}));
} catch (error) {
logger.log('error', error, 'Failed to map GraphQL schema');
const errorMessage = error instanceof Error ? error.message : String(error);
Expand Down
23 changes: 8 additions & 15 deletions reverse_engineering/mappers/typeDefinitions/inputType.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/**
* @import {InputObjectTypeDefinitionNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, REInputTypeDefinition, REPropertiesSchema} from "../../../shared/types/types"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, REInputTypeDefinition} from "../../../shared/types/types"
*/

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

/**
* Maps input object type definitions
Expand All @@ -32,22 +31,16 @@ function getInputObjectTypeDefinitions({ inputObjectTypes = [], definitionCatego
* @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} */ {},
);
const { properties, required } = getFieldsSchema({
fields: [...(inputObjectType.fields || [])],
definitionCategoryByNameMap,
fieldsOrder,
});

return {
type: 'input',
name: inputObjectType.name.value,
properties: convertedProperties,
properties,
required,
description: inputObjectType.description?.value || '',
typeDirectives: mapDirectivesUsage({ directives: [...(inputObjectType.directives || [])] }),
Expand Down
23 changes: 8 additions & 15 deletions reverse_engineering/mappers/typeDefinitions/interface.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/**
* @import {InterfaceTypeDefinitionNode} from "graphql"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, REInterfaceDefinition, REPropertiesSchema} from "../../../shared/types/types"
* @import {DefinitionNameToTypeNameMap, FieldsOrder, REInterfaceDefinition} from "../../../shared/types/types"
*/

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

/**
Expand All @@ -31,22 +30,16 @@ function getInterfaceDefinitions({ interfaces = [], definitionCategoryByNameMap,
* @returns {REInterfaceDefinition} The mapped interface type definition
*/
function mapInterface({ interfaceType, definitionCategoryByNameMap, fieldsOrder }) {
const properties = interfaceType.fields
? interfaceType.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} */ {},
);
const { properties, required } = getFieldsSchema({
fields: [...(interfaceType.fields || [])],
definitionCategoryByNameMap,
fieldsOrder,
});

return {
type: 'interface',
name: interfaceType.name.value,
properties: convertedProperties,
properties,
required,
description: interfaceType.description?.value || '',
typeDirectives: mapDirectivesUsage({ directives: [...(interfaceType.directives || [])] }),
Expand Down
21 changes: 7 additions & 14 deletions reverse_engineering/mappers/typeDefinitions/objectType.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
* @import {DefinitionNameToTypeNameMap, FieldsOrder, REObjectTypeDefinition, REPropertiesSchema} from "../../../shared/types/types"
*/

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

/**
Expand All @@ -31,22 +30,16 @@ function getObjectTypeDefinitions({ objectTypes = [], definitionCategoryByNameMa
* @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} */ {},
);
const { properties, required } = getFieldsSchema({
fields: [...(objectType.fields || [])],
definitionCategoryByNameMap,
fieldsOrder,
});

return {
type: 'object',
name: objectType.name.value,
properties: convertedProperties,
properties,
required,
description: objectType.description?.value || '',
typeDirectives: mapDirectivesUsage({ directives: [...(objectType.directives || [])] }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
* InterfaceStructureType,
* REInterfaceDefinition,
* REInputTypeDefinition,
* InputStructureType} from "../../../shared/types/types"
* InputStructureType,
* DefinitionNameToTypeNameMap} 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');
Expand All @@ -36,11 +36,10 @@ const { getInputObjectTypeDefinitions } = require('./inputType');
* @param {DefinitionNode[]} params.typeDefinitions - The type definitions nodes
* @param {FieldsOrder} params.fieldsOrder - The fields order
* @param {string[]} params.rootTypeNames - The root type names
* @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
* @returns {REModelDefinitionsSchema} The mapped type definitions
*/
function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames }) {
const definitionCategoryByNameMap = getDefinitionCategoryByNameMap({ nodes: typeDefinitions });

function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames, definitionCategoryByNameMap }) {
const directives = getDirectiveTypeDefinitions({
directives: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.DIRECTIVE_DEFINITION }),
});
Expand Down
8 changes: 3 additions & 5 deletions shared/types/fe.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ObjectLikeDefinition,
EnumDefinition,
EnumValue,
EntityDetails,
} from './shared';

export type FEStatement = {
Expand Down Expand Up @@ -113,13 +114,10 @@ export type RootTypeNamesParameter = {
subscription: string;
};

type EntityDetails = {
operationType?: string;
typeDirectives?: DirectivePropertyData[];
};
type FEEntityDetails = EntityDetails<DirectivePropertyData[]>;

export type EntityIdToJsonSchemaMap = Record<string, string>;
export type EntityIdToPropertiesMap = Record<string, [EntityDetails]>;
export type EntityIdToPropertiesMap = Record<string, [FEEntityDetails]>;

// API parameters

Expand Down
Loading