Gqlgen: Provide a configuration option to generate embedded types

Created on 15 Mar 2019  路  9Comments  路  Source: 99designs/gqlgen

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.

enhancement

Most helpful comment

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?

All 9 comments

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

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`

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?

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.

Not stale

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.

Not stale

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.

Not stale

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cemremengu picture cemremengu  路  3Comments

RobertoOrtis picture RobertoOrtis  路  3Comments

huanghantao picture huanghantao  路  3Comments

cajax picture cajax  路  4Comments

theoks picture theoks  路  3Comments