Skip to content
This repository has been archived by the owner on Sep 3, 2021. It is now read-only.

[Question/Suggestion] Autogeneration of Mutations for relations to Interface types #182

Closed
wimklerkx opened this issue Jan 16, 2019 · 4 comments · Fixed by #374
Closed
Labels
Interface Issues relating to Interface types and InlineFragments

Comments

@wimklerkx
Copy link

wimklerkx commented Jan 16, 2019

question: What is the best practice for resolving Mutations for interfaced types?

Example schema:

interface LegalPerson {
	identifier: ID
	name: String!
}

type Person implements LegalPerson {
	identifier: ID
	name: String!
	birthDate: Date
}

type Organization implements LegalPerson  {
	identifier: ID
	name: String!
	foundingDate: Date
}

type CreativeWork {
	identifier: ID
	title: String!
	author: LegalPerson
	responsiblePerson: Person
	publicRepository: Organization
} 

The makeAugmentedSchema() function will autogenerate AddCreativeWorkResponsiblePerson and AddCreativeWorkPublicRepository mutations and resolvers.

But it will not autogenerate the AddCreativeWorkAuthor mutation and resolver.

——————————

So far I’ve come up with something along these lines:

type Mutation {
  AddCreativeWorkAuthor(from:_CreativeWorkInterfaceInput, to:_LegalPersonInput): _AddLegalPersonPayload
}

input _LegalPersonInput {
  identifier: ID!
  type: LegalPersonType!
}

type _AddLegalPersonPayload {
  from: CreativeWork
  to: LegalPerson
}

enum LegalPersonType {
  Person
  Organization
}

Mutation: {
  AddCreativeWorkAuthor (object, params, ctx, resolveInfo) {
    let session = driver.session();
    let query = "MATCH (`creativeWork_from`:`" + params.from.type + "` {identifier: $from.identifier})” +
      " MATCH (`legalPerson_to`: `" + params.to.type + "` {identifier: $to. identifier})" +
      " CREATE (`creativeWork_from`)-[`author_relation`:`AUTHOR`]->(`legalPerson_to`)" +
      " RETURN { from: `creativeWork_from` ,to: `legalPerson_to` } AS `_AddLegalPersonPayload`;"

    let promise = session.run(query, params)
      .then( result => {
        let rt = result.records.map(record => {
          let data = record.get("_AddLegalPersonPayload");
          return {from:data.from.properties, to:data.to.properties};
        });

        return rt[0];
      })
      .catch(function (error) {console.log(error);});

    return promise;
  }
}

But this feels quite cumbersome;
Plus: my schema has many interfaced types, some of which will also need relations to other interfaced/unioned types - adding another layer of complications

There must be a better way

—————————————

Could, maybe, a solution be found on the basis of this proposal?:

If a node would carry the Interface (or Union) label(s) as well, it might be possible to autogenerate the Add/Update/Delete resolvers for relations to/from Interface/Union types.
This could only work if the identifier ID is globally unique, as proposed in the GraphQL specification.

As it would be able to autogenerate an AddCreativeWorkAuthor mutation resolver with a cypher query like this:

MATCH (`creativeWork_from`:`CreativeWork` {identifier: $from.identifier})
      	MATCH (`legalPerson_to`:`LegalPerson` {identifier: $to.identifier)
      	CREATE (`creativeWork_from`)-[`author_relation`:`AUTHOR`]->(`legalPerson_to `)
      	RETURN `author_relation` { from: `creativeWork_from ` { .title } ,to: `legalPerson_to ` { .name }  } AS `_AddLegalPersonPayload `;
@wimklerkx wimklerkx reopened this Jan 16, 2019
@wimklerkx wimklerkx changed the title Autogeneration of Mutations for relations to Interface types [Question/Suggestion] Autogeneration of Mutations for relations to Interface types Jan 16, 2019
@daghan
Copy link

daghan commented Mar 20, 2019

@wimklerkx Do you have a sharable working code? Or do you know if there is any official solution to this (other that what you've outlined above)?

I have the exact same problem. I looked into to graphql-s2s but it is not useful for relations to interfaces.

@wimklerkx
Copy link
Author

wimklerkx commented Mar 24, 2019

@daghan

@wimklerkx Do you have a sharable working code? Or do you know if there is any official solution to this (other that what you've outlined above)?

First, currently there is some work being done on this and on related 'interface' issues:
#210

In the meantime I have fiddled with the idea presented above, but abandoned it as I found myself picking the library apart and was limited by time constraints

I found a way to keep going forward by customising all 'GET' queries and concat subqueries for all implementation types of an interface (by inspecting the schema object). But that's quite cumbersome and potentially slows things down.

Another quirk then led to having to create schema unions for each interface, like this:
union MediaObjectInterfaced = AudioObject | VideoObject | ImageObject
And use these unions in the schema instead of the Interfaces
Which in turn leads to trouble with query fragments

All in all I would not advice to take this road, and I'm looking forward to a real solution :)

@johnymontana johnymontana added the Interface Issues relating to Interface types and InlineFragments label Jun 3, 2019
@hataraxy
Copy link

Thanks @wimklerkx for your findings.
I also have the same issue and same needs.
And thanks @johnymontana for your amazing work 👍 ! I'm looking forward to the fix 😺

@ChristiaanScheermeijer
Copy link
Contributor

Hi @johnymontana, are there any plans to support interfaced relations? As for the latest version, I see that the mutation queries are created, but produces the following error:

Abstract type Camera must resolve to an Object type at runtime for field _AddCameraManFavoriteCameraPayload.to with value { id: \"19fe68b5-c6e7-475b-b9a2-bfa4e5d6c569\" }, received \"undefined\". Either the Camera type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.

The above error is coming from the "movies" example code.

Just thinking out loud; would it be possible to tell the query which type is going to be linked in the "to" input? For example;

mutation {
    AddCameraManFavoriteCamera(
        from: {
            userId:"d72ce434-c00e-4444-94d2-f4583ad1f14d"
        },
        to: {
            id:"19fe68b5-c6e7-475b-b9a2-bfa4e5d6c569",
            typename: "NewCamera"
        }) {
        from {
            userId
        }
        to {
            id
        }
    }
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Interface Issues relating to Interface types and InlineFragments
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants