web-ridge / gqlgen-sqlboiler

This is a plugin for gqlgen to generate converts + filter queries and resolvers for sqlboiler
MIT License
74 stars 13 forks source link

NewResolverPlugin issue #48

Open sjhitchner opened 3 years ago

sjhitchner commented 3 years ago

Hey, finally got time to come back to using gqlgen-sqlboiler.

I'm running into another issue.

Here is my convert_plugin.go

package main

import (
    "fmt"
    "os"

    "github.com/99designs/gqlgen/api"
    "github.com/99designs/gqlgen/codegen/config"
    gbgen "github.com/web-ridge/gqlgen-sqlboiler/v3"
)

func main() {
    cfg, err := config.LoadConfigFromDefaultLocations()
    if err != nil {
        fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
        os.Exit(2)
    }

    output := gbgen.Config{
        Directory: "generated/graphql/helpers",
        // Directory:   "helpers", // supports root or sub directories
        PackageName: "helpers",
    }

    backend := gbgen.Config{
        Directory: "generated/db/models",
        // Directory:   "models",
        PackageName: "models",
    }

    frontend := gbgen.Config{
        Directory: "generated/graphql/models",
        // Directory:   "graphql_models",
        PackageName: "gqlmodels",
    }

    if err = gbgen.SchemaWrite(
        gbgen.SchemaConfig{
            BoilerModelDirectory: backend,
            // Directives:           []string{"IsAuthenticated"},
            // GenerateBatchCreate:  false, // Not implemented yet
            GenerateMutations:   true,
            GenerateBatchDelete: false,
            GenerateBatchUpdate: false,
        },
        "schema/schema.graphqls",
        gbgen.SchemaGenerateConfig{
            MergeSchema: false, // uses three way merge to keep your customization
        },
    ); err != nil {
        fmt.Println("error while trying to generate schema.graphql")
        fmt.Fprintln(os.Stderr, err.Error())
        os.Exit(3)
    }

    if err = api.Generate(cfg,
        api.AddPlugin(gbgen.NewConvertPlugin(
            output,   // directory where convert.go, convert_input.go and preload.go should live
            backend,  // directory where sqlboiler files are put
            frontend, // directory where gqlgen models live
            gbgen.ConvertPluginConfig{
                //UseReflectWorkaroundForSubModelFilteringInPostgresIssue25: true, // see issue #25 on GitHub
            },
        )),
        api.AddPlugin(gbgen.NewResolverPlugin(
            output,
            backend,
            frontend,
            "", // leave empty if you don't have auth
        )),
    ); err != nil {
        fmt.Println("error while trying generate resolver and converts")
        fmt.Fprintln(os.Stderr, err.Error())
        os.Exit(3)
    }
}

When I run it I get the following output/error

% go run convert_plugin.go
...
9:53PM DBG write GraphQL schema to disk bytes=36923 file=schema/schema.graphqls
9:53PM DBG [convert] get boiler models
9:53PM DBG [convert] get extra's from schema
9:53PM DBG [convert] get model with information
9:53PM DBG [convert] render preload.gotpl
9:53PM DBG [convert] render convert.gotpl
9:53PM DBG [convert] render convert_input.gotpl
9:53PM DBG [convert] render filter.gotpl
9:53PM DBG [convert] render sort.gotpl
9:54PM DBG [resolver] get boiler models
9:54PM DBG [resolver] get models with information
9:54PM DBG [resolver] generate file
error while trying generate resolver and converts
resolvergen: convert.gotpl: template: convert.gotpl:30:18: executing "convert.gotpl" at <.Enums>: can't evaluate field Enums in type *gbgen.ResolverBuild
exit status 3

It seems strange to me that the resolver is using the convert.gotpl so I dug in a little further.

This Render statement doesn't include a Template option is this ok? https://github.com/web-ridge/gqlgen-sqlboiler/blob/049ffb39018c5e0e866ae4ff8ec91f217f2a9268/resolver.go#L191

Locally I modified that file to

        err := templates.Render(templates.Options{
            Template:    getTemplate("resolver.gotpl"),
            PackageName: data.Config.Resolver.Package,
            PackageDoc: `
                // This file will be automatically regenerated based on the schema, any resolver implementations
                // will be copied through when generating and any unknown code will be moved to the end.`,
            Filename: filename,
            Data:     resolverBuild,
            Packages: data.Config.Packages,
        })

When I run convert_plugin.go using the modified file I get the following output

% go run convert_plugin.go
...
9:57PM DBG write GraphQL schema to disk bytes=36923 file=schema/schema.graphqls
9:58PM DBG [convert] get boiler models
9:58PM DBG [convert] get extra's from schema
9:58PM DBG [convert] get model with information
9:58PM DBG [convert] render preload.gotpl
9:58PM DBG [convert] render convert.gotpl
9:58PM DBG [convert] render convert_input.gotpl
9:58PM DBG [convert] render filter.gotpl
9:58PM DBG [convert] render sort.gotpl
gofmt failed on schema.resolvers.go: /Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:475:8: expected 'IDENT', found '=' (and 607 more errors)
9:58PM DBG [resolver] get boiler models
9:58PM DBG [resolver] get models with information
9:58PM DBG [resolver] generate file
gofmt failed on schema.resolvers.go: /Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:64:8: expected 'IDENT', found '=' (and 911 more errors)
error while trying generate resolver and converts
validation failed: packages.Load: /Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:64:8: expected 'IDENT', found '='
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:64:8: missing constant value
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:64:10: expected ';', found ""
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:69:8: expected 'IDENT', found '='
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:69:8: missing constant value
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:69:10: expected ';', found ""
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:74:8: expected 'IDENT', found '='
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:74:8: missing constant value
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:74:10: expected ';', found ""
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:79:8: expected 'IDENT', found '='
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:79:8: missing constant value
/Users/steve/src/github.com/sjhitchner/soapcalc/backend/graphql/resolver/schema.resolvers.go:79:10: expected ';', found ""

It looks like the template executes correctly but the resolver code generated are not valid Go.

const inputKey = "input"

const  = ""

    func (r *mutationResolver) CreateAdditive (ctx context.Context, input gmodels.AdditiveCreateInput) (*gmodels.AdditivePayload, error) {
    }

const  = ""

    func (r *mutationResolver) UpdateAdditive (ctx context.Context, id string, input gmodels.AdditiveUpdateInput) (*gmodels.AdditivePayload, error) {
    }

const  = ""

    func (r *mutationResolver) DeleteAdditive (ctx context.Context, id string) (*gmodels.AdditiveDeletePayload, error) {
    }

const  = ""

    func (r *mutationResolver) CreateAdditiveInventory (ctx context.Context, input gmodels.AdditiveInventoryCreateInput) (*gmodels.AdditiveInventoryPayload, error) {
    }

const  = ""

    func (r *mutationResolver) UpdateAdditiveInventory (ctx context.Context, id string, input gmodels.AdditiveInventoryUpdateInput) (*gmodels.AdditiveInventoryPayload, error) {
    }

Any ideas?

RichardLindhout commented 3 years ago

Maybe I should release the development version today it has a lot of bug fixes. Anyway the code you send should never be called since multiple schemas are not supported yet (but is there since I copied code).

I will release a new version today where the resolver and template code is simplified!

sjhitchner commented 3 years ago

Awesome, looking forward to the updated version. I'll take a look through the code today and try to understand why it's entering the multiple schema block.

Also, I have one other question. It is possible to disable generating an edges/connections schema in v3? For my particular projects edges/connections add unneeded complexity. I took a look through the code but didn't see an obvious flag.

Thanks again!

RichardLindhout commented 3 years ago

It should be in gqlgen.yml, in the new version it does not look at this file but generates only 1 file for now.

@sjhitchner I think I'll keep the edges since we need them. I won't work on disabling this via a flag. But feel free to make a pull request which has a flag for this or something like that but be sure to make it for everything (the schema generator + resolver + converts)

Most of the time pagination is needed after a while in a project so I think it's good + it's fast with cursor-based pagination out of the box e.t.c.

Do you not need pagination or do you think the connections/edges are just too complex? ( I agree on that, but it has some reasons for that)

RichardLindhout commented 3 years ago

I forgot about this, I'll try to release tomorrow!

sjhitchner commented 3 years ago

I definitely think pagination support is essential. The edges/connections model does provide that but with much more complex queries. I believe React/Relay requires edges/connections. But I've just used Apollo client so far which seems much simpler (I am NOT an expert with JS/React, so my opinions might be invalid).

I've added pagination in the past by adding limit and from parameters on the list options. That seemed to work fine and give me the functionality I needed.

I can see the reason to conform to a more industry standard approach. I just was curious if library supported removing them.

RichardLindhout commented 3 years ago

Yeah to be honest with you I think the Relay specification is overengineered to handle very niche cases. But if you have them you can query the cursor on them. The arguments to the connections are simplified since relay does not support both forward and backward pagination as arguments.

So if you have first: 10, after: "sljdflksj==" it will use forward pagination and last:10 before: "slsl==" it will use backward pagination.

It implements the following spec: https://relay.dev/graphql/connections.htm

RichardLindhout commented 3 years ago

New version is released. Please follow upgrade notes and let me know if this fixed it for you.

cfg, err := config.LoadConfigFromDefaultLocations() (convert_plugin.go) should be placed after if err := gbgen.SchemaWrite(gbgen.SchemaConfig{

Like in the updated README.md maybe this resulted in your error.

sjhitchner commented 3 years ago

Awesome, thanks for the update. Definitely making some progress but I have encountered a few more issues I'm trying to sort out.

Missing imports:

.../helpers/convert.go
.../helpers/convert_input.go
.../helpers/filter.go
.../helpers/sort.go
../helpers/preload.go

Are missing the graphql model and db model import statements in my case:

gqlmodels github.com/sjhitchner/soapcalc/backend/generated/graphql/models"
dbmodels github.com/sjhitchner/soapcalc/backend/generated/db/models"

Also resolver.go needs to import the generated graphql code

// needs to import "gm github.com/sjhitchner/soapcalc/backend/generated/graphql"

func (r *Resolver) Mutation() fm.MutationResolver { return &mutationResolver{r} }     // should be gm not fm
func (r *Resolver) Query() fm.QueryResolver       { return &queryResolver{r} }             // should be gm not fm

type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }

I think this may be just be template issues.

Making the manual changes above I get everything to compile and runs!

sjhitchner commented 3 years ago

I think this fixes the issues I was experiencing in the above post

https://github.com/web-ridge/gqlgen-sqlboiler/pull/50

sjhitchner commented 3 years ago

The only issue I'm facing right now is that when I run the plugin go run convert_plugin.go two resolver files are generated

resolver.go
schema.resolver.go

There are a number of errors about how various objects are defined twice and schema.resolver.go is missing a number of imports.

If, after running the plugin, I delete schema.resolver.go everything compiles.

Is it expected behaviour for both resolvers to be generated. If not, any ideas what I have misconfigured?

RichardLindhout commented 3 years ago

I think the gqlgen file should only contain this for the resolver key

resolver:
  filename: resolver.go
  type: Resolver