Schema Stitching resolve conflict by adding prefix
Asked Answered
K

2

7

So I have this two schemas

Schema1

type Permission {
    relation: Relation
}

enum Relation {
    ONE
    TWO
    THREE
}

Schema2

type Permission {
    relation: Relation
}

enum Relation {
    FOUR
    FIVE
    SIX
}  

The expect result is something similar to: (but I'm open to different ideas) The queries I would like to make after the merge are:

{
    permissions{
        relation
    }
}

And get a result like

"permissions": [
  {
    "relation": "ONE"
  },
  {
    "relation": "SIX"
  }
]

or

"permissions": [
  {
    "relation": "schema1ONE"
  },
  {
    "relation": "schema2SIX"
  }
]

And mutations like:

mutation{
  createPermission(
    relation: ONE
  ){
    relation
  }
}

mutation{
  createPermission(
    relation: SIX
  ){
    relation
  }
}

or

mutation{
  createPermission(
    relation: schema1ONE
  ){
    relation
  }
}

mutation{
  createPermission(
    relation: schema2SIX
  ){
    relation
  }
}

I'm trying using the transformSchema function on graphql-tools but can't quite figure it out correctly:

const Schema1 = await getRemoteSchema('schema1_url', 'schema1');
const Schema2 = await getRemoteSchema('schema2_url', 'schema2');

const schemas = [Schema1, Schema2]

const schema = mergeSchemas({
  schemas: schemas,
  resolvers: {}
});

getRemoteSchema definition

export const getRemoteSchema = async (uri: string, schemaName: string): Promise<GraphQLSchema> => {
  const httpLink = new HttpLink({ uri, fetch });

  const schema = await introspectSchema(httpLink);

  const executableSchema = makeRemoteExecutableSchema({
    schema,
    httpLink,
  });

  // transform schema by renaming root fields and types
  const renamedSchema = transformSchema(
    executableSchema,
    [
      new RenameTypes(name => {
        if (name == 'Relation') {
          return schemaName + name
        } else {
          return name
        }
      }),
      // new RenameRootFields((operation, name) => `${schemaName}_${name}`)
    ]
  );

  return renamedSchema;
}    

I made this glitch https://glitch.com/edit/#!/schema-stitching-conflict So it's easier to see the problem.

Kristelkristen answered 8/2, 2019 at 3:14 Comment(1)
So for one part is the renaming of the types, but also the joining of the permission typesKristelkristen
M
1

You need both RenameTypes and RenameRootFields transforms, RenameTypes to transform the typenames

from: Permission and Relation (The colliding types),

to: schema1_Permission, schema2_Permission

and: schema1_Relation, schema1_Relation

RenameRootFields to transform the Query names for those types

from: permission(id: ID!): Permission

to: schema1_permission(id: ID!): schema1_Permission and schema2_permission(id: ID!): schema2_Permission

and: permissions: [Permission]

to: schema1_permissions: [schema1_Permission] and schema2_permissions: [schema2_Permission]

The transform would be something like:

const {
  makeExecutableSchema,
  addMockFunctionsToSchema,
  transformSchema,
  RenameTypes,
  RenameRootFields
} = require('graphql-tools');

const schema1 = makeExecutableSchema({
  typeDefs: `
    type Permission {
      id: ID!
      text: String
      relation: Relation
    }

    type Query {
      permissions: [Permission]
      permission(id: ID!): Permission
    }

    enum Relation {
      ONE
      TWO
      THREE
    }
  `
});

addMockFunctionsToSchema({ schema: schema1 });

const renamedSchema1 = transformSchema(
  schema1,
  [
    new RenameTypes(name => {
      if (name == 'Relation' || name == 'Permission') {
        return 'schema1_' + name
      } else {
        return name
      }
    }, { renameBuiltins: false, renameScalars: true }),
    new RenameRootFields((_op, name) => {
      return name.includes('ermission') ? `schema1_${name}` : name
    })
  ]
);

references: https://www.apollographql.com/docs/graphql-tools/schema-transforms/ https://www.apollographql.com/docs/graphql-tools/schema-stitching/

Missi answered 14/11, 2019 at 15:52 Comment(0)
S
0

Currently I don't see an easy-to-go way to achieve your desired behaviour using graphql-tools because in the implementation of mergeSchemas() the option onTypeConflict was first deprecated and later removed, even though it still exists on the public interface. With that option we were able to simply pass a callback that was aware of the conflicting types and their corresponding ASTs.

transformSchema() however, as you try to use it, will only rename the enum type names but not the enum values. You will most likely need to implement your own transformation instead of using a pre-defined one to achieve your goals. I'd like to recommend having a look at the implementation of ConvertEnumValues though. This might give you a better sense of how to walk and manipulate the AST to your needs when implementing your own Transform.

For example I'd consider an implementation which keeps track of all GraphQlEnumTypes that it has seen and deep merges them, if it encounters a name collision. Either you keep track using variables within module scope or using instance properties in the Transform. If you do the latter, don't forget to instantiate it in advance and to pass it to subsequent tranformSchema() calls by reference.

Shatter answered 12/2, 2019 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.