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

Provide a configuration option to generate embedded types #626

Open
steebchen opened this issue Mar 15, 2019 · 11 comments
Open

Provide a configuration option to generate embedded types #626

steebchen opened this issue Mar 15, 2019 · 11 comments
Labels
binder Related to mapping Go to GraphQL types enhancement New feature or request

Comments

@steebchen
Copy link
Contributor

This schema

interface User {
	id: ID!
	email: String!
}

type Customer implements User {
	id: ID!
	email: String!

	favoriteEmployee: Employee
}

type Employee implements User {
	id: ID!
	email: String!

	favoriteCustomers: [Customer!]!
}

produces

type User interface {
	IsUser()
}

type Customer struct {
	ID               string    `json:"id"`
	Email            string    `json:"email"`
	FavoriteEmployee *Employee `json:"favoriteEmployee"`
}

func (Customer) IsUser() {}

type Employee struct {
	ID                string     `json:"id"`
	Email             string     `json:"email"`
	FavoriteCustomers []Customer `json:"favoriteCustomers"`
}

func (Employee) IsUser() {}

This works fine, but it's cumbersome to work with when converting from an internal type (e.g. User) to a Customer or Employee.

This forces me to declare those types by myself and import them in gqlgen:

type User struct { // my original user type
	ID     string `json:"id"`
	Email  string `json:"email"`
}

func (User) IsUser() {}

type Customer struct {
	*User
	favoriteEmployee *Employee
}

type Employee struct {
	*User
	favoriteCustomers []Customer
}
# ...
models:
  User:
    model: github.com/steebchen/api/prisma.User
  Customer:
    model: github.com/steebchen/api/prisma.Customer
  Employee:
    model: github.com/steebchen/api/prisma.Employee

This also works fine, but it's a bit annoying to define it manually and importing it manually. It also gets more cumbersome if the schema gets more complex with more user types and more complex schemas (for example when implementing multiple interfaces).

It would be great if there was an option so that embedded structs could be automatically generated.

@mathewbyrne
Copy link
Contributor

Do you have any thoughts on what the configuration for this would look like?

@steebchen
Copy link
Contributor Author

steebchen commented Mar 15, 2019

Not quite sure what would be best, I'll just throw some suggestions

What about a simple embedded_types: true globally?

That could be maybe too restrictive, so maybe a completely dedicated option for interfaces:

embedded:
  - Customer
  - Employee

or maybe in the top-level object:

models:
  User:
    model: github.com/steebchen/keskin-api/prisma.User
    embed:
      - Customer
      - Employee

or separately and specify a type to embed:

models:
  Customer:
    embedded: true

or the same as above but more explicit:

models:
  Customer:
    embed: github.com/steebchen/keskin-api/prisma.User
    # this should mean generate the model and embed `User`

@vektah
Copy link
Collaborator

vektah commented Mar 16, 2019

An alternative approach would be something like:

  User:
    embed: github.com/steebchen/keskin-api/prisma.User
    interface: github.com/steebchen/keskin-api/prisma.IUser 

Anything implementing the interface would automatically embed the given type, if one is set.

Multiple inheritance is a thing too, eg from the spec

interface NamedEntity {
  name: String
}

interface ValuedEntity {
  value: Int
}

type Person implements NamedEntity {
  name: String
  age: Int
}

type Business implements NamedEntity & ValuedEntity {
  name: String
  value: Int
  employeeCount: Int
}

could generate

type INamedEntity interface {
    IsNamedEntity()
}
type NamedEntityBase struct {
    Name string
}

func (NamedEntityBase) IsNamedEntity() {}

type IValuedEntity interface {
    IsValuedEntity()
}

type ValuedEntityBase struct {
    Value string
}

func (ValuedEntityBase) IsValuedEntity() {}

type Person struct {
  *NamedEntityBase
   Age int
}

type Business struct {
  *NamedEntityBase
  *ValuedEntityBase
   EmployeeCount int
}

@lwc implemented the current interface generation, I wonder if he has thoughts?

@mathewbyrne mathewbyrne added the enhancement New feature or request label Mar 17, 2019
@stale
Copy link

stale bot commented Aug 28, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Aug 28, 2019
@steebchen
Copy link
Contributor Author

Not stale

@stale stale bot removed the stale label Aug 28, 2019
@stale
Copy link

stale bot commented Oct 27, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Oct 27, 2019
@steebchen
Copy link
Contributor Author

Not stale

@stale stale bot removed the stale label Oct 27, 2019
@stale
Copy link

stale bot commented Dec 26, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Dec 26, 2019
@steebchen
Copy link
Contributor Author

Not stale

@stale stale bot removed the stale label Dec 26, 2019
@lwc lwc added the binder Related to mapping Go to GraphQL types label Sep 17, 2021
@Pokerkoffer
Copy link

Pokerkoffer commented Dec 19, 2021

Are there any plans to implement this? I'm facing the same issue and it should be an easy task to solve it.

Edit: The workaround solution above doesn't work anymore. Gqlgen expects an interface

merging type systems failed: unable to bind to interface: <path>/graph/model.User is not an interface exit status 1

A possible workaround is to specify an interface IUser, that every type implements and an additional User type.

interface IUser {
# ...
}

type User implements IUser {
# ...
}

type Customer implements IUser {
# ...
}

now, gqlgen correctly generates a User struct

@adrianlungu
Copy link

Created a PR helping with this, it does not cover all possible edge cases, but it's under a configuration flag so it can be turned on on demand and potentially helping in most cases.

Hope to get any feedback on the PR and hopefully have this merged asap

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
binder Related to mapping Go to GraphQL types enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants