Skip to content

Commit

Permalink
Merge pull request #5 from Just4Ease/dev
Browse files Browse the repository at this point in the history
feat: GraphRPC new updates
  • Loading branch information
Just4Ease committed Oct 7, 2021
2 parents 606e98d + d1912a2 commit 140cc90
Show file tree
Hide file tree
Showing 33 changed files with 1,682 additions and 225 deletions.
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
----

## About
GraphRPC is simply GraphQL as your RPC Contract Input & Output Layer.

GraphRPC is simply GraphQL as your RPC Contract Input & Output Layer and NATS.io as your data transmission via events. (
Request/Reply, Pub&Sub )

- No proto contract corruption on any update
- Programming language agnostic
- One entry point
- Custom headers on query || mutations
- Custom headers on query || mutations
- Client code generation ( thanks to https://github.com/Yamashou/gqlgenc 🚀 )
- Nats.io integration
- todo: Server CodeGen ( using https://github.com/99designs/gqlgen )


- Server CodeGen ( using https://github.com/99designs/gqlgen )

## Appreciation & Inspirations

Expand All @@ -25,9 +25,19 @@ GraphRPC is simply GraphQL as your RPC Contract Input & Output Layer.
- Axon - https://github.com/Just4Ease/axon
- AxonRPC - https://github.com/Just4Ease/axonrpc


## TODO

- [] Subscriptions
- [] Add Server generator following 99designs/gqlgen's implementation
- [] Subscriptions for clients (WIP)
- TLS on server startup.

## How to use

```shell script
# To generate server code
# It is advised to add this command to a makefile or run the tools.go once
printf '// +build tools\npackage tools\nimport _ "github.com/Just4Ease/graphrpc/generator/cmd"' | gofmt > tools.go
go mod tidy

# To actually generate resolvers and server entrypoint file.
go run github.com/Just4Ease/graphrpc/generator/cmd --filename server.go
```
49 changes: 0 additions & 49 deletions _labs/pubs/pubs.go

This file was deleted.

2 changes: 1 addition & 1 deletion client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func TestUnmarshal(t *testing.T) {
t.Parallel()
r := &fakeRes{}
err := unmarshal([]byte(withBadDataFormat), r, true)
require.EqualError(t, err, "failed to decode data into response {\"data\": \"notAndObject\"}: : : : : json: cannot unmarshal string into Go value of type client.fakeRes")
require.EqualError(t, err, "failed to decode data into response {\"data\": \"notAndObject\"}: json: cannot unmarshal string into Go value of type client.fakeRes")
})

t.Run("bad data format", func(t *testing.T) {
Expand Down
48 changes: 48 additions & 0 deletions config/gqlgen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- graph/schemas/*.graphql

# Where should the generated server code go?
exec:
filename: graph/generated.go
package: graph

# Uncomment to enable federation
# federation:
# filename: graph/generated/federation.go
# package: generated

# Where should any generated models go?
model:
filename: graph/types.go
package: graph

# Where should the resolver implementations go?
resolver:
layout: follow-schema
dir: graph
package: graph

# Optional: turn on use `gqlgen:"fieldName"` tags in your models
# struct_tag: json

# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: true

# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true

# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
- "{{.}}/graph"

# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
Int:
model:
- github.com/99designs/gqlgen/graphql.Int64
3 changes: 1 addition & 2 deletions generator/client_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (c *Clients) AddClient(opts ...ClientGeneratorOption) error {
}
}

model := path.Clean(fmt.Sprintf("%s/%s/models.go", c.generateToDirectory, clientGenerator.PackagePath))
model := path.Clean(fmt.Sprintf("%s/%s/types.go", c.generateToDirectory, clientGenerator.PackagePath))
generated := path.Clean(fmt.Sprintf("%s/%s/generated.go", c.generateToDirectory, clientGenerator.PackagePath))

cfgParams := &config.GraphRPCClientConfig{
Expand Down Expand Up @@ -255,7 +255,6 @@ func generateClientCode(ctx context.Context, g *ClientGenerator, option ...api.O
}

if err := config.LoadSchema(ctx, g.cfg, g.Conn, client.SetRemoteServiceName(g.RemoteServiceName)); err != nil {
fmt.Print(err, " skem \n")
return fmt.Errorf("failed to load schema: %w", err)
}

Expand Down
65 changes: 65 additions & 0 deletions generator/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"fmt"
"github.com/99designs/gqlgen/graphql"
"github.com/Just4Ease/graphrpc/generator"
"github.com/gookit/color"
"github.com/urfave/cli/v2"
"io/ioutil"
"log"
"os"
)

var genCmd = &cli.Command{
Name: "generate",
Usage: "generate a graphql server based on schema",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "verbose, v", Usage: "show logs"},
&cli.StringFlag{Name: "filename, f", Usage: "the server filename you want code to be generated into"},
},
Action: func(ctx *cli.Context) error {
fileName := "server.go"

if serverFilename := ctx.String("filename"); serverFilename != "" {
fileName = serverFilename
} else {
color.Yellow.Print("⚡️ Server filename not provided, defaulting to server.go \n")
}

generator.GenerateGraphRPCServer(fileName)
return nil
},
}

func main() {
app := cli.NewApp()
app.Name = "graphrpcgen"
app.Usage = genCmd.Usage
app.Description = `
This is a library for quickly creating strictly typed graphql servers in golang.
See https://gqlgen.com/ for a getting started guide.
Note, this library will scaffold a GraphRPC Server for you.
See https://github.com/Just4Ease/graphrpc 🦾 🐙 🐉 🔦 🕸
`
app.HideVersion = true
app.Flags = genCmd.Flags
app.Version = graphql.Version
app.Before = func(context *cli.Context) error {
if context.Bool("verbose") {
log.SetFlags(0)
} else {
log.SetOutput(ioutil.Discard)
}
return nil
}

app.Action = genCmd.Action
app.Commands = []*cli.Command{genCmd}

if err := app.Run(os.Args); err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
}
55 changes: 54 additions & 1 deletion generator/server_generator.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
package generator

// TODO: implement server generator
import (
"fmt"
"github.com/99designs/gqlgen/api"
genCfg "github.com/99designs/gqlgen/codegen/config"
"github.com/Just4Ease/graphrpc/generator/servergen"
"github.com/Just4Ease/graphrpc/internal/code"
"github.com/gookit/color"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"os"
)

func GenerateGraphRPCServer(fileName string) {

pkgName := code.ImportPathForDir(".")
if pkgName == "" {
_, _ = fmt.Fprintln(os.Stderr, "unable to determine import path for current directory, you probably need to run go mod init first\"")
os.Exit(4)
return
}

configByte, err := servergen.InitConfig(pkgName)
if err != nil {
return
}

cfg := genCfg.DefaultConfig()

if err := yaml.UnmarshalStrict(configByte, cfg); err != nil {
_, _ = fmt.Fprintln(os.Stderr, errors.Wrap(err, "unable to parse config").Error())
os.Exit(4)
return
}

if err := genCfg.CompleteConfig(cfg); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err.Error())
os.Exit(4)
return
}

if err := servergen.PrepareSchema("graph/schemas/"); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err.Error())
os.Exit(4)
return
}

_, _ = fmt.Fprint(os.Stdout, color.Green.Sprint("✅ Successfully generated resolvers.\n"))

if err := api.Generate(cfg, api.AddPlugin(servergen.New(fileName, pkgName))); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err.Error())
os.Exit(4)
return
}
}
66 changes: 66 additions & 0 deletions generator/servergen/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package servergen

import (
"bytes"
"text/template"
)

var configTemplate = template.Must(template.New("gqlgen.yml").Parse(`
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- graph/schemas/*.graphql
# Where should the generated server code go?
exec:
filename: graph/generated.go
package: graph
# Uncomment to enable federation
# federation:
# filename: graph/generated/federation.go
# package: generated
# Where should any generated models go?
model:
filename: graph/types.go
package: graph
# Where should the resolver implementations go?
resolver:
layout: follow-schema
dir: graph
package: graph
# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json
# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: true
# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true
# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
- "{{.}}/graph"
# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
Int:
model:
- github.com/99designs/gqlgen/graphql.Int64
`))

func InitConfig(pkgName string) ([]byte, error) {
var buf bytes.Buffer
if err := configTemplate.Execute(&buf, pkgName); err != nil {
panic(err)
}

return buf.Bytes(), nil
}
Loading

0 comments on commit 140cc90

Please sign in to comment.