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

using sqlboiler, null.string fields in structs, field resolvers created #646

Closed
frederikhors opened this issue Mar 25, 2019 · 10 comments
Closed
Labels
support please use https://discord.gg/DYEq3EMs4U for support questions

Comments

@frederikhors
Copy link
Collaborator

frederikhors commented Mar 25, 2019

I have this table:

CREATE TABLE "user_profiles" (
  "id" INT NOT NULL,
  "user_id" int NOT NULL,
  "username" text,
  "img" text,
)

SQLBoiler is generating this entity:

import "github.com/volatiletech/null"

type UserProfile struct {
	ID        int
	UserID    int
	Username  null.String
	Img       null.String
}

My schema.graphql:

type User{
    id : ID!
    userProfile : UserProfile!
}

type UserProfile{
    username : String
    img : String
}

My gqlgen.yml:

UserProfile:
    model: project/entities.UserProfile

Expected Behaviour

I thought gqlgen must generate just:

func (r *userResolver) UserProfile(ctx context.Context, obj *entities.User) (*entities.UserProfile, error) {

Actual Behavior

gqlgen now generates also:

func (r *userProfileResolver) Username(ctx context.Context, obj *entities.UserProfile) (*string, error) {
	panic("not implemented")
}
func (r *userProfileResolver) Img(ctx context.Context, obj *entities.UserProfile) (*string, error) {
	panic("not implemented")
}

and in logging during generation:

String has type compatible with github.com/volatiletech/null.String
String has type compatible with github.com/volatiletech/null.String

If I use this in gqlgen.yml:

String:
  model:
    - github.com/99designs/gqlgen/graphql.String
    - github.com/volatiletech/null.String

it works (doesn't generate Username and Img resolvers) but something is wrong in gqlgen_generated.go at runtime:

gqlgen_generated.go:3041:2: not enough arguments to return
gqlgen_generated.go:3041:11: ec.unmarshalInputString undefined (type *executionContext has no field or method unmarshalInputString)
gqlgen_generated.go:3048:11: ec._String undefined (type *executionContext has no field or method _String)

Where am I doing wrong?

@mathewbyrne
Copy link
Contributor

You will need to implement a custom scalar to null.Stringtake a look at our documentation on custom scalars for third party types for how to do this.

We could potentially be better at detecting that your type mapping wont work at runtime and not generate invalid code.

@frederikhors
Copy link
Collaborator Author

@mathewbyrne oh, thanks a lot for the hint.

I have read but I am too inexperienced to be able to create one myself.

How can I create a custom scalar for null.String? In the Unmarshal part what can I return? Maybe nil?

Do you have a nullable string scalar already?

@frederikhors
Copy link
Collaborator Author

@mathewbyrne I have made progress, but something is still not entirely clear to me.

As soon I understand and solve the problem I can write a small Wiki for newbies like me.

I wrote this:

package graphql

import (
	"github.com/99designs/gqlgen/graphql"
	"github.com/volatiletech/null"
	"io"
	"strconv"
)

func MarshalMyNullString(ns null.String) graphql.Marshaler {
	return graphql.WriterFunc(func(w io.Writer) {
		if !ns.Valid {
			_, err := w.Write([]byte("null")) // Is this correct?
			DieIfErr(err)
		}
		_, err := io.WriteString(w, strconv.Quote(ns.String))
		DieIfErr(err)
	})
}

func UnmarshalMyNullString(v interface{}) (null.String, error) {
	
	// What to do here?

	// return v.(null.String), nil
}

TWO QUESTIONS:

  • Is Marshal() correct and without performance problems?

  • As I read that Unmarshal() is only used if the scalar appears as an input (right?):

    • What I can check there?
    • I need to use v interface{}?
    • Can it ever be that my input model is an object with null.String fields?

Maybe I don't understand the point entirely here, but I'm close, @vektah.

Thanks a lot and please appreciate my commitment.

@vektah
Copy link
Collaborator

vektah commented Mar 26, 2019

Is Marshal() correct and without performance problems?

Probably for most cases, but there are some fairly sublte differences between strconv.Quote and valid json strings. Would probably recommend just casing and calling the default string implementation.

func MarshalMyNullString(ns null.String) graphql.Marshaler {
	if !ns.Valid {
		// this is also important, so we can detect if this scalar is used in a not null context and return an appropriate error
		return graphql.Null  
	}
	return graphql.MarshalString(ns.String)
}

As I read that Unmarshal() is only used if the scalar appears as an input (right?):

Yeah, you should implement both. Scalars can be used in input types, arguments and directives which all require unmarshal. Its just the reverse operation, at its simplest:

func UnmarshalMyNullString(v interface{}) (null.String, error) {
	if v == nil {
		return null.String{Valid: false}, nil
	}
	// again you can delegate to the default implementation to save yourself some work.
	s, err := graphql.UnmarshalString(v)
	return null.String{Value: s}, err
}

Can it ever be that my input model is an object with null.String fields?

By default the first implementation for a model will be chosen when generating models or argument signatures, but for any model you declare yourself (even inputs) you can use null.String in the code and gqlgen will try to find the right marshal.

@mathewbyrne
Copy link
Contributor

Hopefully @vektah's response solves your issue. I'm going to close this out.

@frederikhors
Copy link
Collaborator Author

@mathewbyrne yes, thanks. Amazing, gqlgen is a stellar project! :)

@vektah, you mean return null.String{String: s}, err instead of return null.String{Value: s}, err, right?

@vektah vektah added the support please use https://discord.gg/DYEq3EMs4U for support questions label May 15, 2019
@RichardLindhout
Copy link
Contributor

@frederikhors How did you workaround this?

@RichardLindhout
Copy link
Contributor

I just let gqlgen generate pure models and I'm generating a custom convert plugin for it which generates a convert file.

It's work in progress, and I work on this when I have some spare time
https://github.com/web-ridge/gqlgen-sqlboiler

@RichardLindhout
Copy link
Contributor

Schermafbeelding 2019-09-24 om 23 00 56

@RichardLindhout
Copy link
Contributor

It's more advanced right know and can create pure converts from boiler to gqlgen if the name and type are the same thing (e.g. *string and null.String)
Schermafbeelding 2019-11-08 om 16 37 35

Schermafbeelding 2019-11-08 om 16 48 37

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
support please use https://discord.gg/DYEq3EMs4U for support questions
Projects
None yet
Development

No branches or pull requests

4 participants