Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fix abstraction #34

Merged
merged 3 commits into from
Sep 10, 2024
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
81 changes: 46 additions & 35 deletions packages/graphql-mesh/patches/@graphql-tools+stitch+9.0.3.patch
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ index 5e752ad..3b487f5 100644
const currentNamedType = (0, graphql_1.getNamedType)(c.type);
if (finalNamedType.toString() !== currentNamedType.toString()) {
diff --git a/node_modules/@graphql-tools/stitch/cjs/typeCandidates.js b/node_modules/@graphql-tools/stitch/cjs/typeCandidates.js
index c915942..ae923fe 100644
index c915942..f617985 100644
--- a/node_modules/@graphql-tools/stitch/cjs/typeCandidates.js
+++ b/node_modules/@graphql-tools/stitch/cjs/typeCandidates.js
@@ -119,6 +119,33 @@ function buildTypes({ typeCandidates, directives, stitchingInfo, rootTypeNames,
Expand All @@ -33,9 +33,9 @@ index c915942..ae923fe 100644
+ otherCandidates.forEach((otherCandidate) => {
+ // Add fields from other candidates to the final interface
+ Object.keys(otherCandidate.type._fields).forEach(field => {
+ if (finalI.type._fields[field] === undefined) {
+ finalI.type._fields[field] = otherCandidate.type._fields[field];
+ }
+ if (finalI.type._fields[field] === undefined) {
+ finalI.type._fields[field] = otherCandidate.type._fields[field];
+ }
+ })
+ });
+ typeCandidates[typeName] = [finalI];
Expand All @@ -49,7 +49,7 @@ index c915942..ae923fe 100644
typeMap[typeName] = (0, mergeCandidates_js_1.mergeCandidates)(typeName, typeCandidates[typeName], typeMergingOptions);
}
else {
@@ -128,6 +155,61 @@ function buildTypes({ typeCandidates, directives, stitchingInfo, rootTypeNames,
@@ -128,6 +155,72 @@ function buildTypes({ typeCandidates, directives, stitchingInfo, rootTypeNames,
typeMap[typeName] = candidateSelector(typeCandidates[typeName]).type;
}
}
Expand All @@ -64,48 +64,59 @@ index c915942..ae923fe 100644
+ let duplicateFields = {};
+
+ typeInterfaces.forEach(i => {
+ const iFields = typeMap[i.name].getFields();
+ Object.keys(iFields).forEach(keyName => {
+ if (uniqueFields[keyName] === undefined) {
+ uniqueFields[keyName] = "defined";
+ }
+ else {
+ duplicateFields[keyName] = iFields[keyName];
+ }
+ })
+ const iFields = typeMap[i.name].getFields();
+ Object.keys(iFields).forEach(keyName => {
+ if (uniqueFields[keyName] === undefined) {
+ uniqueFields[keyName] = "defined";
+ }
+ else {
+ duplicateFields[keyName] = iFields[keyName];
+ }
+ })
+ })
+ // Ensure duplicate fields are consistent across implementations
+ Object.keys(duplicateFields).forEach(field => {
+ if (type.getFields()[field] !== undefined) {
+ type._fields[field] = duplicateFields[field];
+ }
+ typeInterfaces.forEach(i => {
+ const iFields = typeMap[i.name].getFields();
+ if (type.getFields()[field] !== undefined) {
+ type._fields[field] = duplicateFields[field];
+ }
+ typeInterfaces.forEach(i => {
+ const iFields = typeMap[i.name].getFields();
+
+ if (iFields[field] !== undefined) {
+ typeMap[i.name]._fields[field] = duplicateFields[field];
+ }
+ })
+ if (iFields[field] !== undefined) {
+ typeMap[i.name]._fields[field] = duplicateFields[field];
+ }
+ })
+ })
+ }
+ }
+ });
+ // Ensure object types implement all fields from their interfaces
+ Object.values(typeMap).forEach((type) => {
+ if (type.constructor.name === "GraphQLObjectType") {
+ const typeInterfaces = type.getInterfaces();
+ if (type.constructor.name === "GraphQLObjectType") {
+ const typeInterfaces = type.getInterfaces();
+
+ if (typeInterfaces.length !== 0) {
+ type._fields = type.getFields();
+ if (typeInterfaces.length !== 0) {
+ type._fields = type.getFields();
+
+ typeInterfaces.forEach(i => {
+ const iFields = typeMap[i.name].getFields();
+ Object.keys(iFields).forEach(keyName => {
+ type._fields[keyName] = iFields[keyName];
+ })
+ })
+ }
+ }
+ typeInterfaces.forEach(i => {
+ const iFields = typeMap[i.name].getFields();
+ Object.keys(iFields).forEach(keyName => {
+ type._fields[keyName] = iFields[keyName];
+ })
+ })
+ }
+ }
+ })
+ // Add a default type for all the interfaces
+ Object.values(typeMap).forEach((type) => {
+ if (type.constructor.name === "GraphQLInterfaceType") {
+ typeMap[`Default__${type.name}`] = new graphql_1.GraphQLObjectType({
+ name: `Default__${type.name}`,
+ fields: {},
+ interfaces: [type]
+ })
+ typeMap[`Default__${type.name}`]._fields = typeMap[`${type.name}`]._fields
+ }
+ })
+
return (0, utils_1.rewireTypes)(typeMap, directives);
Expand Down
20 changes: 10 additions & 10 deletions packages/graphql-mesh/patches/@omnigraph+json-schema+0.97.4.patch
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
diff --git a/node_modules/@omnigraph/json-schema/cjs/addRootFieldResolver.js b/node_modules/@omnigraph/json-schema/cjs/addRootFieldResolver.js
index 8006747..dfc6825 100644
old mode 100644
new mode 100755
index 8006747..f2af17c
--- a/node_modules/@omnigraph/json-schema/cjs/addRootFieldResolver.js
+++ b/node_modules/@omnigraph/json-schema/cjs/addRootFieldResolver.js
@@ -35,6 +35,23 @@ function addHTTPRootFieldResolver(schema, field, logger, globalFetch, { path, op
@@ -35,6 +35,21 @@ function addHTTPRootFieldResolver(schema, field, logger, globalFetch, { path, op
const interpolatedBaseUrl = string_interpolation_1.stringInterpolator.parse(endpoint, interpolationData);
const interpolatedPath = string_interpolation_1.stringInterpolator.parse(path, interpolationData);
let fullPath = (0, url_join_1.default)(interpolatedBaseUrl, interpolatedPath);
+ let subPath;
+ /**
+ * FIXME: This is a hack to follow link in the response
+ * In case where the root object contains several links,
+ * we need to follow the correct link which match the index of field in the path
+ */
+ if (root && root?.followsLink?.length) {
+
+ // HANDLE HATEOAS LINKS
+ if (root && root?.followLinks?.length) {
+ const index = info?.path?.key?.match(/_(\d+)_/)?.[1];
+ subPath = root.followsLink?.[index]?.followLink;
+ subPath = root.followLinks?.[index]?.followLink;
+ fullPath = interpolatedBaseUrl + subPath;
+ } else if (root && root.followLink) {
+ subPath = root.followLink;
+ fullPath = interpolatedBaseUrl + subPath;
+ }
+ if (root && !subPath) {
+ return
+ return;
+ }
+
const headers = {};
for (const headerName in globalOperationHeaders) {
const nonInterpolatedValue = globalOperationHeaders[headerName];
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ describe('ConfigFromSwaggers tests', () => {
// Test function to get interfaces with their children
it('should return interfaces with children correctly', () => {
const expectedInterfacesWithChildren: Record<string, string[]> = {
Vehicle: ['Car', 'Bike']
Vehicle: ['Car', 'Bike', 'Default__Vehicle']
}
expect(instance.getInterfacesWithChildren()).toEqual(expectedInterfacesWithChildren)
})
Expand Down
1 change: 1 addition & 0 deletions packages/graphql-mesh/utils/configFromSwaggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export default class ConfigFromSwaggers {
}
})
}
this.interfacesWithChildren[schemaKey].push(`Default__${schemaKey.split('_')[0]}`)
}
})
return this.interfacesWithChildren
Expand Down
64 changes: 31 additions & 33 deletions packages/graphql-mesh/utils/generateTypeDefsAndResolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,13 @@ export const generateTypeDefsAndResolversFromSwagger = (
// Filter for special keys such as 'x-links' and 'x-graphql-prefix-schema-with'
.filter(isSpecialKey)
.forEach(([key, value]) => {

// HANDLE SCHEMA PREFIXATION
if (key === 'x-graphql-prefix-schema-with') {
const schemaType = Object.keys(interfacesWithChildren).includes(schemaKey)
? 'interface'
: 'type'

// Add a prefixSchema directive to the type definition
typeDefs += `extend ${schemaType} ${schemaKey} @prefixSchema(prefix: "${value}") { dummy: String }\n`

// If it's an interface, add the dummy property to each of its children too
if (schemaType === 'interface') {
interfacesWithChildren[schemaKey].forEach((children) => {
Expand All @@ -67,7 +64,6 @@ export const generateTypeDefsAndResolversFromSwagger = (
})
}
}

// HANDLE HATEOAS LINKS PROCESSING
if (key === 'x-links') {
const trimedSchemaKey = trimLinks(schemaKey)
Expand Down Expand Up @@ -151,12 +147,14 @@ export const generateTypeDefsAndResolversFromSwagger = (
}`,

resolve: (root: any, args: any, context: any, info: any) => {
const hateoasLink: any = Object.entries(root._links).find(
(item) => item[0] === xLinkName
)?.[1]

if (hateoasLink?.href) {
root = { ...root, followLink: hateoasLink.href }
if (root._links) {
const hateoasLink: any = Object.entries(root._links).find(
(item) => item[0] === xLinkName
)?.[1]

if (hateoasLink?.href) {
root = { ...root, followLink: hateoasLink.href }
}
}

if (paramsToSend.length) {
Expand Down Expand Up @@ -223,12 +221,14 @@ export const generateTypeDefsAndResolversFromSwagger = (
}`,

resolve: (root: any, args: any, context: any, info: any) => {
const hateoasLink: any = Object.entries(root._links).find(
(item) => item[0] === xLinkName
)?.[1]
if (root._links) {
const hateoasLink: any = Object.entries(root._links).find(
(item) => item[0] === xLinkName
)?.[1]

if (hateoasLink?.href) {
root = { ...root, followLink: hateoasLink.href }
if (hateoasLink?.href) {
root = { ...root, followLink: hateoasLink.href }
}
}

if (paramsToSend.length) {
Expand Down Expand Up @@ -257,21 +257,23 @@ export const generateTypeDefsAndResolversFromSwagger = (

// Resolvers for _linksList and _actionsList
if (Object.keys(subResolver).length) {
subTypeDefs += /* GraphQL */ `_linksList: [LinkItem]\n`
subResolver['_linksList'] = {
selectionSet: /* GraphQL */ `
if (_linksItems !== '') {
subTypeDefs += /* GraphQL */ `_linksList: [LinkItem]\n`
subResolver['_linksList'] = {
selectionSet: /* GraphQL */ `
{
_links {
${_linksItems}
}
}`,
resolve: (root: any) => {
return Object.keys(root?._links || {})
.filter((key) => root._links[key]?.href)
.map((key) => ({
rel: key,
href: root._links[key]?.href
}))
resolve: (root: any) => {
return Object.keys(root?._links || {})
.filter((key) => root._links[key]?.href)
.map((key) => ({
rel: key,
href: root._links[key]?.href
}))
}
}
}
if (_actionsItems !== '') {
Expand Down Expand Up @@ -302,7 +304,7 @@ export const generateTypeDefsAndResolversFromSwagger = (
// Delete the additional typeDefs section if no new fields have been added
subTypeDefs = subTypeDefs.replace(`extend ${schemaType} ${trimedSchemaKey} {\n}\n`, '')

if (subTypeDefs !== "") {
if (subTypeDefs !== '') {
typeDefs += subTypeDefs
resolvers[trimedSchemaKey] = subResolver
}
Expand Down Expand Up @@ -336,16 +338,12 @@ export const generateTypeDefsAndResolversFromSwagger = (
}
})

// Interfaces need to have the additional '__resolveType' property
// Resolvers of interfaces need to have the additional '__resolveType' property
resolvers[trimedSchemaKey].__resolveType = (res, _, schema) => {
if (res.__typename) {
if (res.__typename && !interfacesWithChildren[res.__typename]) {
return res.__typename
}
const returnTypeName = schema.returnType.name
const parentVersion = returnTypeName.split('_')[returnTypeName.split('_').length - 1]
return parentVersion !== trimedSchemaKey
? `${interfacesWithChildren[schema.returnType.name][1]}_${parentVersion}`
: `${interfacesWithChildren[schema.returnType.name][1]}`
return `Default__${schema.returnType.name}`
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions patches/@graphql-tools+batch-execute+9.0.2.patch
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ index dfe8afc..b60987c 100644
info: requests[0].info,
operationType,
- rootValue: requests[0].rootValue,
+ // FIXME: Extend the rootValue with the followsLink to save followLink for all requests that are merged and executed in parallel
+ rootValue: { ...requests[0].rootValue, followsLink: requests.map((request) => ({ followLink: request.rootValue.followLink })) },
+ // Extend the rootValue with an array of the available links
+ rootValue: { ...requests[0].rootValue, followLinks: requests.map((request) => ({ followLink: request.rootValue.followLink })) },
};
}
exports.mergeRequests = mergeRequests;
Loading