Skip to content

Commit

Permalink
graphql-go example integration
Browse files Browse the repository at this point in the history
Example `graphql-go` integration that is using https://github.com/dariuszkuc/graphql/releases/tag/v0.9.0-federation

NOTE: this is a temporary solution while we are waiting for `graphql-go` PRs to get merged ([651](graphql-go/graphql#651), [652](graphql-go/graphql#652) and [653](graphql-go/graphql#653)).

Resolves: apollographql#74
  • Loading branch information
dariuszkuc committed Oct 31, 2022
1 parent 38fd701 commit f7a3bce
Show file tree
Hide file tree
Showing 11 changed files with 660 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ The following open-source GraphQL server libraries and hosted subgraphs provide
<tr><th width="300">Library</th><th>Federation 1 Support</th><th>Federation 2 Support</th></tr>
</thead>
<tbody>
<tr><td><a href="https://github.com/graphql-go/graphql">GraphQL Go</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🟢</td></tr><tr><th>repeatable @key</th><td>🟢</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🔲</td></tr></table></td><td><table><tr><th>@link</th><td>🟢</td></tr><tr><th>@shareable</th><td>🟢</td></tr><tr><th>@tag</th><td>🟢</td></tr><tr><th>@override</th><td>🟢</td></tr><tr><th>@inaccessible</th><td>🟢</td></tr></table></td></tr>
<tr><td><a href="https://gqlgen.com">gqlgen</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🟢</td></tr><tr><th>repeatable @key</th><td>🟢</td></tr><tr><th>@requires</th><td>🔲</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🟢</td></tr></table></td><td><table><tr><th>@link</th><td>🟢</td></tr><tr><th>@shareable</th><td>🟢</td></tr><tr><th>@tag</th><td>🟢</td></tr><tr><th>@override</th><td>🟢</td></tr><tr><th>@inaccessible</th><td>🟢</td></tr></table></td></tr>
</tbody>
</table>
Expand Down
9 changes: 9 additions & 0 deletions implementations/graphql-go/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM golang:1.19

WORKDIR /go/src/server
COPY . .

RUN go get -d -v ./...
RUN go install -v ./...

CMD go run server.go
6 changes: 6 additions & 0 deletions implementations/graphql-go/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
products:
# must be relative to the root of the project
build: implementations/graphql-go
ports:
- 4001:4001
10 changes: 10 additions & 0 deletions implementations/graphql-go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module graphql-go-compatibility

go 1.19

require (
github.com/graphql-go/graphql v0.8.0
github.com/graphql-go/handler v0.2.3
)

replace github.com/graphql-go/graphql => github.com/dariuszkuc/graphql v0.9.0-federation
4 changes: 4 additions & 0 deletions implementations/graphql-go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/dariuszkuc/graphql v0.9.0-federation h1:dHxP3Z+wX/hRkj/3jaxGXb7iG+Zw3fjW1frQbgsq3lI=
github.com/dariuszkuc/graphql v0.9.0-federation/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ=
github.com/graphql-go/handler v0.2.3 h1:CANh8WPnl5M9uA25c2GBhPqJhE53Fg0Iue/fRNla71E=
github.com/graphql-go/handler v0.2.3/go.mod h1:leLF6RpV5uZMN1CdImAxuiayrYYhOk33bZciaUGaXeU=
3 changes: 3 additions & 0 deletions implementations/graphql-go/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fullName: GraphQL Go
language: Go
documentation: https://github.com/graphql-go/graphql
47 changes: 47 additions & 0 deletions implementations/graphql-go/model/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package model

type User struct {
// AverageProductsCreatedPerYear *int `json:"averageProductsCreatedPerYear"`
Email string `json:"email"`
Name *string `json:"name"`
TotalProductsCreated *int `json:"totalProductsCreated"`
YearsOfEmployment int `json:"yearsOfEmployment"`
}

type CaseStudy struct {
CaseNumber string `json:"caseNumber"`
Description *string `json:"description"`
}

type DeprecatedProduct struct {
Sku string `json:"sku"`
Package string `json:"package"`
Reason *string `json:"reason"`
CreatedBy *User `json:"createdBy"`
}

type Product struct {
ID string `json:"id"`
Sku *string `json:"sku"`
Package *string `json:"package"`
Variation *ProductVariation `json:"variation"`
Dimensions *ProductDimension `json:"dimensions"`
CreatedBy *User `json:"createdBy"`
Notes *string `json:"notes"`
Research []*ProductResearch `json:"research"`
}

type ProductDimension struct {
Size *string `json:"size"`
Weight *float64 `json:"weight"`
Unit *string `json:"unit"`
}

type ProductResearch struct {
Study *CaseStudy `json:"study"`
Outcome *string `json:"outcome"`
}

type ProductVariation struct {
ID string `json:"id"`
}
56 changes: 56 additions & 0 deletions implementations/graphql-go/products.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.0",
import: ["@extends", "@external", "@inaccessible", "@key", "@override", "@provides", "@requires", "@shareable", "@tag"]
)

type Product @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }") {
id: ID!
sku: String
package: String
variation: ProductVariation
dimensions: ProductDimension
createdBy: User @provides(fields: "totalProductsCreated")
notes: String @tag(name: "internal")
research: [ProductResearch!]!
}

type DeprecatedProduct @key(fields: "sku package") {
sku: String!
package: String!
reason: String
createdBy: User
}

type ProductVariation {
id: ID!
}

type ProductResearch @key(fields: "study { caseNumber }") {
study: CaseStudy!
outcome: String
}

type CaseStudy {
caseNumber: ID!
description: String
}

type ProductDimension @shareable {
size: String
weight: Float
unit: String @inaccessible
}

extend type Query {
product(id: ID!): Product
deprecatedProduct(sku: String!, package: String!): DeprecatedProduct @deprecated(reason: "Use product query instead")
}

extend type User @key(fields: "email") {
averageProductsCreatedPerYear: Int @requires(fields: "totalProductsCreated yearsOfEmployment")
email: ID! @external
name: String @override(from: "users")
totalProductsCreated: Int @external
yearsOfEmployment: Int! @external
}
78 changes: 78 additions & 0 deletions implementations/graphql-go/resolver/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package resolver

import "graphql-go-compatibility/model"

var federationSku = "federation"
var federationPackage = "@apollo/federation"

var studioSku = "studio"

var productSize = "small"
var productWeight = 1.0
var productUnit = "kg"

var products = []*model.Product{
{
ID: "apollo-federation",
Sku: &federationSku,
Package: &federationPackage,
Variation: &model.ProductVariation{ID: "OSS"},
Dimensions: &model.ProductDimension{
Size: &productSize,
Weight: &productWeight,
Unit: &productUnit,
},
},
{
ID: "apollo-studio",
Sku: &studioSku,
Package: nil,
Variation: &model.ProductVariation{ID: "platform"},
Dimensions: &model.ProductDimension{
Size: &productSize,
Weight: &productWeight,
Unit: &productUnit,
},
},
}

var userName = "Jane Smith"
var totalProductsCreated = 1337

var users = []*model.User{
{
Email: "support@apollographql.com",
Name: &userName,
TotalProductsCreated: &totalProductsCreated,
},
}

var DefaultUser = users[0]

var deprecationReason = "Migrate to Federation V2"

var deprecatedProducts = []*model.DeprecatedProduct{
{
Sku: "apollo-federation-v1",
Package: "@apollo/federation-v1",
Reason: &deprecationReason,
},
}

var federationStudyDescription = "Federation Study"
var studioStudyDescription = "Studio Study"

var research = []*model.ProductResearch{
{
Study: &model.CaseStudy{
CaseNumber: "1234",
Description: &federationStudyDescription,
},
},
{
Study: &model.CaseStudy{
CaseNumber: "1235",
Description: &studioStudyDescription,
},
},
}
146 changes: 146 additions & 0 deletions implementations/graphql-go/resolver/resolvers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package resolver

import (
"math"

"graphql-go-compatibility/model"
)

func FindDeprecatedProductBySkuAndPackage(sku string, packageArg string) (*model.DeprecatedProduct, error) {
for i := range deprecatedProducts {
if deprecatedProducts[i].Sku == sku && deprecatedProducts[i].Package == packageArg {
return deprecatedProducts[i], nil
}
}
return nil, nil
}

func FindProductById(id string) (*model.Product, error) {
for i := range products {
if products[i].ID == id {
return products[i], nil
}
}
return nil, nil
}

func FindProductBySkuAndPackage(sku string, packageArg string) (*model.Product, error) {
for i := range products {
if *products[i].Sku == sku && *products[i].Package == packageArg {
return products[i], nil
}
}
return nil, nil
}

func FindProductBySkuAndVariationID(sku string, variationID string) (*model.Product, error) {
for i := range products {
if *products[i].Sku == sku && products[i].Variation.ID == variationID {
return products[i], nil
}
}
return nil, nil
}

func FindProductResearchByProductId(product *model.Product) ([]*model.ProductResearch, error) {
switch product.ID {
case "apollo-federation":
return research[:1], nil
case "apollo-studio":
return research[1:], nil
default:
return nil, nil
}
}

func CalculateAverageProductsCreatedPerYear(user *model.User) (*int, error) {
if user.TotalProductsCreated == nil {
return nil, nil
}
var avgProductsCreated = int(math.Round(float64(*user.TotalProductsCreated) / float64(user.YearsOfEmployment)))
return &avgProductsCreated, nil
}

// entity resolvers

// @key(fields: "sku package")
func DeprecatedProductEntityResolver(params map[string]interface{}) (*model.DeprecatedProduct, error) {
sku, skuOk := params["sku"].(string)
pkg, pkgOk := params["package"].(string)
if skuOk && pkgOk {
return FindDeprecatedProductBySkuAndPackage(sku, pkg)
}
return nil, nil
}

// @key(fields: "id")
// @key(fields: "sku package")
// @key(fields: "sku variation { id }")
func ProductEntityResolver(params map[string]interface{}) (*model.Product, error) {
id, ok := params["id"].(string)
if ok {
return FindProductById(id)
} else {
sku, skuOk := params["sku"].(string)
if skuOk {
pkg, pkgOk := params["package"].(string)
if pkgOk {
return FindProductBySkuAndPackage(sku, pkg)
} else {
variation, varOk := params["variation"].(map[string]interface{})
if varOk {
varId, varIdOk := variation["id"].(string)
if varIdOk {
return FindProductBySkuAndVariationID(sku, varId)
}
}
}
}
}
return nil, nil
}

// @key(fields: "study { caseNumber }")
func ProductResearchEntityResolver(params map[string]interface{}) (*model.ProductResearch, error) {
study, ok := params["study"].(map[string]interface{})
if ok {
caseNumber, caseOk := study["caseNumber"].(string)
if caseOk {
for i := range research {
if research[i].Study.CaseNumber == caseNumber {
return research[i], nil
}
}
}
}
return nil, nil
}

// @key(fields: "email")
func UserEntityResolver(params map[string]interface{}) (*model.User, error) {
email, ok := params["email"].(string)
if ok {
for i := range users {
var user *model.User
if users[i].Email == email {
user = users[i]
}

totalProducts, totalSpecified := params["totalProductsCreated"].(float64)
if totalSpecified {
total := int(totalProducts)
user.TotalProductsCreated = &total
}

yearsOfEmployment, yearsSpecified := params["yearsOfEmployment"].(float64)
if yearsSpecified {
user.YearsOfEmployment = int(yearsOfEmployment)
}

if user != nil {
return user, nil
}
}
}
return nil, nil
}

0 comments on commit f7a3bce

Please sign in to comment.