Slash doesn't generate the right input object

Hi,

I have two types that contain arrays of interfaces.

type Version {
    orkId: String! @search(by: [hash]) @id
    code: String! @search(by: [regexp, hash])
    vid: String!
    gitCommit: String @search(by: [hash])
    next: Version
    previous: Version @hasInverse(field: next)
    ofBranch: Branch! @hasInverse(field: versions)
    publishedFrom: Version @hasInverse(field: publications)
    publications: [Version!]
    checkouts: [Branch!]
    artifacts: [IArtifact!] @hasInverse(field: inVersion)
    createdAt: DateTime!
    lastModified: DateTime
    createdBy: User!
    wayOfCreation: CreationType!
}
type ScriptArtifact implements IArtifact {
    arguments: [String!]!
    inputs: [IObjectArtifact!] @hasInverse(field: inputOf)
    output: [IObjectArtifact!] @hasInverse(field: outputOf)
}

interface IArtifact {
    orkId: String! @search(by: [hash]) @id
    name: String! @search(by: [exact, regexp])
    cuid: String! @search(by: [hash])
    tic: DateTime!
    createdAt: DateTime! @search(by: [year])
    createdBy: User
    createdOnHost: String! @search(by: [hash, regexp])
    inVersion: Version!
    extras: String
}

interface IObjectArtifact {
    inputOf: [ScriptArtifact!]
    outputOf: ScriptArtifact
    saver: ObjectHandler!
    loader: ObjectHandler!
}

For some reason slash decides to generate the artifacts array in the add version input:

But for add script artifact the inputs and outputs are missing:

Can you please check why this is hapenning

Thanks,
Spinelsun

Moved this to Issues>Slash GraphQL because it is a potential bug.

Hi,

Did someone look into it?

You don’t have an ID or @id field in IObjectArtifact. That is the reason it didn’t generate a ref for it.

On the other hand, you have an @id field in IArtifact, that is the reason it generated a ref for it in addVersionInput.

Thanks

2 Likes

Ok,

That leads to another question I asked you once about implementing 2 interface with the same fields.
I have some other types that implement both IObjectArtifact and IArtifact.
If I’ll add an ID for both of them what will happen?

1 Like

@pawan @abhimanyusinghgaur help please :pray:t2:

What is the current behavior that you observe when you add an ID to both of them? If it doesn’t work for you, then we can add a quick fix where if the ID! field name is the same for two implementing interfaces, we should allow it.

@pawan
Currently it doesn’t allow me to add this field in both interfaces
this is the error I get from slash:

resolving updateGQLSchema failed because input:40: Field DatasetObjectArtifact.orkId can only be defined once

Thanks.

We will be adding a fix to allow the behavior as per Pawan’s suggestion:

Ok Thanks
Will it work for @id fields as well?

No, just ID type fields.

Can you explain the reason?

At present, a field in an interface is stored in Dgraph using interfaceName.fieldName predicate, irrespective of which types implement that interface. That particular strategy solves a lot of problems with respect to interface handling in GraphQL on top of DQL.
For example, in your case, you have following schema:

interface IArtifact {
    orkId: String! @search(by: [hash]) @id
    ...
}
type ScriptArtifact implements IArtifact {
    arguments: [String!]!
    inputs: [IObjectArtifact!] @hasInverse(field: inputOf)
    output: [IObjectArtifact!] @hasInverse(field: outputOf)
}

The corresponding DQL schema would be:

type IArtifact {
    IArtifact.orkId
   ...
}
IArtifact.orkId: string @index(hash) .

type ScriptArtifact {
    ScriptArtifact.arguments
    ScriptArtifact.inputs
    ScriptArtifact.output
}
...

So, if there are multiple interfaces with the same field name and a type that implements all those interfaces, then there would be a problem: in which predicate to store the data for the type objects?
Example:

interface I1 {
  f1: String
}
interface I2 {
 f1: String
}
type T implements I1 & I2 {
 f2: Int!
}

That means type T has only two fields f1 & f2. But, while adding data for type T, which predicate to use to store the data for f1: I1.f1 or I2.f1? That is a problem.

This is not a problem with ID, because that maps to uid in Dgraph, and so is not stored as a predicate.

@abhimanyusinghgaur I understand.
It seems like a something that is similar to the Deadly Diamond of Death problem.
In python they solved it by taking the field of the first Inherited interface/class.
Maybe you can take the same approach?

Hi @Luscha, thanks for the suggestion.

The problem with using the field of the first inherited interface would be that the other interfaces won’t find that field in the type. That won’t be the expected behavior from a user’s perspective. Whether I query f1 using I1 or I2, I should always get it on objects of type T. If I use I1.f1 for storage, I won’t get f1 when I query it using I2.

On the other hand, if we were to use T.f1 as the predicate name for the inherited field, then we will no longer have the Diamond inheritance problem. But, that would introduce a lot of complexity in the GraphQL layer.

So, I would instead suggest not to have a design that can cause such problems, if possible.

Hi @spinelsun, We added support to repeat the field name of ID type. You can now have field of ID type and of same name in multiple implementing interfaces.This fix is currently avaliable in master branch and will also be avaliable in 20.11 release.
PR:fix(GraphQL): This PR allows repetition of fields inside implementing type which are present in interface and also allow to inherit field of same name of type ID from multiple interfaces. by JatinDevDG · Pull Request #7053 · dgraph-io/dgraph · GitHub