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

Add support for generic unions #3515

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

enoua5
Copy link
Contributor

@enoua5 enoua5 commented May 26, 2024

Description

This PR merges union types in strawberry.schema.schema_converter.GraphQLCoreCoverter:from_union. This allows creating a more stable interface, as a union can be created between a TypeVar and an annotated union. In the examples in #3393, this changes SomeTypeByIdNotFoundError => SomeTypeByIdResult, the latter of which won't change as more error cases are added.

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Documentation

Issues Fixed or Closed by This PR

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @enoua5 - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

strawberry/schema/schema_converter.py Show resolved Hide resolved
strawberry/schema/schema_converter.py Outdated Show resolved Hide resolved
strawberry/schema/schema_converter.py Outdated Show resolved Hide resolved
@botberry
Copy link
Member

botberry commented May 26, 2024

Thanks for adding the RELEASE.md file!

Here's a preview of the changelog:


Attempt to merge union types during schema conversion.

Here's the tweet text:

🆕 Release (next) is out! Thanks to Jacob Allen for the PR 👏

Get it here 👉 https://strawberry.rocks/release/(next)

enoua5 and others added 3 commits May 26, 2024 13:58
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Copy link

codecov bot commented May 26, 2024

Codecov Report

Attention: Patch coverage is 93.97590% with 5 lines in your changes are missing coverage. Please review.

Project coverage is 96.54%. Comparing base (a7fc70a) to head (b34106e).
Report is 15 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3515      +/-   ##
==========================================
+ Coverage   96.53%   96.54%   +0.01%     
==========================================
  Files         520      523       +3     
  Lines       33243    33430     +187     
  Branches     5527     5544      +17     
==========================================
+ Hits        32090    32276     +186     
+ Misses        920      914       -6     
- Partials      233      240       +7     

Copy link

codspeed-hq bot commented May 26, 2024

CodSpeed Performance Report

Merging #3515 will not alter performance

Comparing enoua5:fix-3393 (b34106e) with main (2f02b49)

Summary

✅ 12 untouched benchmarks

@patrick91
Copy link
Member

@enoua5 I pushed a fix 😊 but I'll try to write a test as well

}

type Query {
someTypeQueries(id: ID!): SomeTypeByIdResult!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type here is still wrong, we probably need to expand the annotated

Comment on lines +1 to +3
Release type: patch

Attempt to merge union types during schema conversion.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the last change this is probably a minor now, since it's a brand new feature (plus a fix) 😊

elif isinstance(type_, StrawberryOptional):
name = self.get_from_type(type_.of_type) + "Optional"
name = self.get_name_from_type(type_.of_type) + "Optional"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed the name of this function 😊

@strawberry.field
def by_id(
self, id: strawberry.ID
) -> Annotated[Union[T, NotFoundError], strawberry.union("ByIdResult")]: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to clarify, this is quite different from the use case below, as this creates a generic union 😊

@enoua5
Copy link
Contributor Author

enoua5 commented May 27, 2024

Thank you for the fixes, @patrick91! It's looking good, and each of the examples in #3393 are now working as expected! 🎉

It sounds like we'll need to change the release file? Does something like "Add support for generic unions" encompass the new changes? As far as examples go for the release file, would copying an example or two from the new tests suffice?

@patrick91
Copy link
Member

@enoua5 yes! I'll change the release file ☺️ it was late yesterday and I was discussing with @bellini666 if there was a way to simplify the code, I'll take another look today

Copy link
Member

@bellini666 bellini666 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noice

@enoua5 enoua5 changed the title Merge unions in schema converter Add support for generic unions May 30, 2024
@enoua5
Copy link
Contributor Author

enoua5 commented May 30, 2024

Just realized we should probably also have a test to make sure unions with multiple generics work. If we think that's needed, I can add one when I get home from work today

@patrick91
Copy link
Member

@enoua5 yes please 😊

@enoua5
Copy link
Contributor Author

enoua5 commented May 31, 2024

Also of note is that this is technically a breaking change. A breaking change that makes output more predictable and stable, but a breaking change nonetheless.
Consider the following schema definition:

from typing import Annotated, Generic, TypeVar
import strawberry


@strawberry.type
class SomeType:
    a: str

@strawberry.type
class NotFoundError:
    id: strawberry.ID
    message: str

T = TypeVar('T')


@strawberry.type
class ObjectQueries(Generic[T]):
    ...

    @strawberry.field
    def by_id(self, id: strawberry.ID) -> Annotated[T | NotFoundError, strawberry.union("ByIdResult")]:
        ...

    ...


@strawberry.type
class Query:

    @strawberry.field
    def some_type_queries(self, id: strawberry.ID) -> ObjectQueries[SomeType]:
        return ObjectQueries(SomeType)
    

schema = strawberry.Schema(Query)

Under the current version of strawberry, this SDL is generated:

type Query {
  someTypeQueries(id: ID!): SomeTypeObjectQueries!
}

type SomeTypeObjectQueries {
  byId(id: ID!): SomeTypeNotFoundError!
}

union SomeTypeNotFoundError = SomeType | NotFoundError

type SomeType {
  a: String!
}

type NotFoundError {
  id: ID!
  message: String!
}

Whereas under this PR, this is the SDL generated:

type Query {
  someTypeQueries(id: ID!): SomeTypeObjectQueries!
}

type SomeTypeObjectQueries {
  byId(id: ID!): SomeTypeByIdResult!
}

union SomeTypeByIdResult = SomeType | NotFoundError

type SomeType {
  a: String!
}

type NotFoundError {
  id: ID!
  message: String!
}

For clarity, that:

6c6
<   byId(id: ID!): SomeTypeNotFoundError!
---
>   byId(id: ID!): SomeTypeByIdResult!
9c9
< union SomeTypeNotFoundError = SomeType | NotFoundError
---
> union SomeTypeByIdResult = SomeType | NotFoundError

While I do this this naming is better and makes it easier to write a consistent API (in the former case, adding a new error type NewError to the union would result in a type name of SomeTypeNotFoundErrorNewError, also breaking code), this could potentially break any code already using this pattern.

@strawberry.field
def by_id(
self, id: strawberry.ID
) -> T | Annotated[U | NotFoundError, strawberry.union("ByIdResult")]: ...
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test captures the combined case of one generic inside and one generic outside the annotation. Both in and both out also work, but I figured the tests would be getting a bit redundant if I included every combination of in and out.

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

Successfully merging this pull request may close these issues.

4 participants