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

Edge case bug while using FilterableConnectionField within a Union Connection #69

Open
FireWhale opened this issue Jun 13, 2022 · 0 comments

Comments

@FireWhale
Copy link

I ran into an interesting bug that is caused by how we calculate the unique key in a NestedFilterableConnectionField:

        # Unique dataloader key for context.
        data_loader_key = tuple((p for p in info.path if isinstance(p, str)))

The bug occurs when there is a polymorphic field that is represented by a Union and a NestedFilterableConnectionField is queried off of the different Union types. The dataloader_key will not be unique even if the root is a different type

Example Setup (Simplified version but hopefully one can fill in the gaps):

class PrimaryRecord(Base):
    id = Column(Integer, primary_key=True, autoincrement=True)
    discriminator = Column(String)
    references = relationship("Reference", secondary='primary_record_reference', backref="primary_records")

class Reference(Base):
    id = Column(Integer, primary_key=True, autoincrement=True)
    url = Column(String, nullable=False)

class PrimaryRecordReference(Base):
    primary_record_id = Column(Integer, ForeignKey('primary_record.id'), primary_key=True)
    reference_id = Column(Integer, ForeignKey('reference.id'), primary_key=True)

class Video(PrimaryRecord):
    id = Column(Integer, ForeignKey(PrimaryRecord.id), primary_key=True)
    __mapper_args__ = {"polymorphic_identity": "Video", 'polymorphic_load': 'inline'}
    title = Column(String, nullable=False)

class Image(PrimaryRecord):
    id = Column(Integer, ForeignKey(PrimaryRecord.id), primary_key=True)
    __mapper_args__ = {"polymorphic_identity": "Image", 'polymorphic_load': 'inline'}
    name = Column(String)

Union classes:

PrimaryRecordUnion(graphene.Union):
    class Meta:
        types=(ImageSQLAlchemyObjectType, VideoSQLAlchemyObjectType)

PrimaryRecordUnionConnection(graphene.Connection):
    class Meta:
        node=PrimaryRecordUnion

union_connection_field = relay.ConnectionField(
    PrimaryRecordUnionConnection
    resolver=resolve_primary_records
)

def resolve_primary_records(record, info, **kwargs):
    return record.primary_records

Sample query:

query { records { edges {
  node {
    __typename
    ... on Video {
      id
      title
      references { edges { node { id } } }
    }
    ... on Image {
      id
      name
      references { edges { node { id } } }
    }
  }
} } }

When using this setup, the data_loader_key for both Image and Video references is 'records', 'edges', 'node', 'references' even though the ModelLoader's parent_model needs to be different (a reference attached to a Video cannot be found when the parent_model is an Image)

I fixed this by simply adding the class name to the unique key, so different parent classes have different data loaders:

        data_loader_key = tuple((p for p in info.path + [root.__class__.__name__] if isinstance(p, str)))

Not sure if it's worth fixing or if there's a better way to do it, but documenting this just in case someone else somehow runs into this particular edge case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant