Skip to content

Commit f7a3bce

Browse files
committed
graphql-go example integration
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
1 parent 38fd701 commit f7a3bce

File tree

11 files changed

+660
-0
lines changed

11 files changed

+660
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ The following open-source GraphQL server libraries and hosted subgraphs provide
5757
<tr><th width="300">Library</th><th>Federation 1 Support</th><th>Federation 2 Support</th></tr>
5858
</thead>
5959
<tbody>
60+
<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>
6061
<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>
6162
</tbody>
6263
</table>

implementations/graphql-go/Dockerfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM golang:1.19
2+
3+
WORKDIR /go/src/server
4+
COPY . .
5+
6+
RUN go get -d -v ./...
7+
RUN go install -v ./...
8+
9+
CMD go run server.go
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
services:
2+
products:
3+
# must be relative to the root of the project
4+
build: implementations/graphql-go
5+
ports:
6+
- 4001:4001

implementations/graphql-go/go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module graphql-go-compatibility
2+
3+
go 1.19
4+
5+
require (
6+
github.com/graphql-go/graphql v0.8.0
7+
github.com/graphql-go/handler v0.2.3
8+
)
9+
10+
replace github.com/graphql-go/graphql => github.com/dariuszkuc/graphql v0.9.0-federation

implementations/graphql-go/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/dariuszkuc/graphql v0.9.0-federation h1:dHxP3Z+wX/hRkj/3jaxGXb7iG+Zw3fjW1frQbgsq3lI=
2+
github.com/dariuszkuc/graphql v0.9.0-federation/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ=
3+
github.com/graphql-go/handler v0.2.3 h1:CANh8WPnl5M9uA25c2GBhPqJhE53Fg0Iue/fRNla71E=
4+
github.com/graphql-go/handler v0.2.3/go.mod h1:leLF6RpV5uZMN1CdImAxuiayrYYhOk33bZciaUGaXeU=
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fullName: GraphQL Go
2+
language: Go
3+
documentation: https://github.com/graphql-go/graphql
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package model
2+
3+
type User struct {
4+
// AverageProductsCreatedPerYear *int `json:"averageProductsCreatedPerYear"`
5+
Email string `json:"email"`
6+
Name *string `json:"name"`
7+
TotalProductsCreated *int `json:"totalProductsCreated"`
8+
YearsOfEmployment int `json:"yearsOfEmployment"`
9+
}
10+
11+
type CaseStudy struct {
12+
CaseNumber string `json:"caseNumber"`
13+
Description *string `json:"description"`
14+
}
15+
16+
type DeprecatedProduct struct {
17+
Sku string `json:"sku"`
18+
Package string `json:"package"`
19+
Reason *string `json:"reason"`
20+
CreatedBy *User `json:"createdBy"`
21+
}
22+
23+
type Product struct {
24+
ID string `json:"id"`
25+
Sku *string `json:"sku"`
26+
Package *string `json:"package"`
27+
Variation *ProductVariation `json:"variation"`
28+
Dimensions *ProductDimension `json:"dimensions"`
29+
CreatedBy *User `json:"createdBy"`
30+
Notes *string `json:"notes"`
31+
Research []*ProductResearch `json:"research"`
32+
}
33+
34+
type ProductDimension struct {
35+
Size *string `json:"size"`
36+
Weight *float64 `json:"weight"`
37+
Unit *string `json:"unit"`
38+
}
39+
40+
type ProductResearch struct {
41+
Study *CaseStudy `json:"study"`
42+
Outcome *string `json:"outcome"`
43+
}
44+
45+
type ProductVariation struct {
46+
ID string `json:"id"`
47+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
extend schema
2+
@link(
3+
url: "https://specs.apollo.dev/federation/v2.0",
4+
import: ["@extends", "@external", "@inaccessible", "@key", "@override", "@provides", "@requires", "@shareable", "@tag"]
5+
)
6+
7+
type Product @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }") {
8+
id: ID!
9+
sku: String
10+
package: String
11+
variation: ProductVariation
12+
dimensions: ProductDimension
13+
createdBy: User @provides(fields: "totalProductsCreated")
14+
notes: String @tag(name: "internal")
15+
research: [ProductResearch!]!
16+
}
17+
18+
type DeprecatedProduct @key(fields: "sku package") {
19+
sku: String!
20+
package: String!
21+
reason: String
22+
createdBy: User
23+
}
24+
25+
type ProductVariation {
26+
id: ID!
27+
}
28+
29+
type ProductResearch @key(fields: "study { caseNumber }") {
30+
study: CaseStudy!
31+
outcome: String
32+
}
33+
34+
type CaseStudy {
35+
caseNumber: ID!
36+
description: String
37+
}
38+
39+
type ProductDimension @shareable {
40+
size: String
41+
weight: Float
42+
unit: String @inaccessible
43+
}
44+
45+
extend type Query {
46+
product(id: ID!): Product
47+
deprecatedProduct(sku: String!, package: String!): DeprecatedProduct @deprecated(reason: "Use product query instead")
48+
}
49+
50+
extend type User @key(fields: "email") {
51+
averageProductsCreatedPerYear: Int @requires(fields: "totalProductsCreated yearsOfEmployment")
52+
email: ID! @external
53+
name: String @override(from: "users")
54+
totalProductsCreated: Int @external
55+
yearsOfEmployment: Int! @external
56+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package resolver
2+
3+
import "graphql-go-compatibility/model"
4+
5+
var federationSku = "federation"
6+
var federationPackage = "@apollo/federation"
7+
8+
var studioSku = "studio"
9+
10+
var productSize = "small"
11+
var productWeight = 1.0
12+
var productUnit = "kg"
13+
14+
var products = []*model.Product{
15+
{
16+
ID: "apollo-federation",
17+
Sku: &federationSku,
18+
Package: &federationPackage,
19+
Variation: &model.ProductVariation{ID: "OSS"},
20+
Dimensions: &model.ProductDimension{
21+
Size: &productSize,
22+
Weight: &productWeight,
23+
Unit: &productUnit,
24+
},
25+
},
26+
{
27+
ID: "apollo-studio",
28+
Sku: &studioSku,
29+
Package: nil,
30+
Variation: &model.ProductVariation{ID: "platform"},
31+
Dimensions: &model.ProductDimension{
32+
Size: &productSize,
33+
Weight: &productWeight,
34+
Unit: &productUnit,
35+
},
36+
},
37+
}
38+
39+
var userName = "Jane Smith"
40+
var totalProductsCreated = 1337
41+
42+
var users = []*model.User{
43+
{
44+
45+
Name: &userName,
46+
TotalProductsCreated: &totalProductsCreated,
47+
},
48+
}
49+
50+
var DefaultUser = users[0]
51+
52+
var deprecationReason = "Migrate to Federation V2"
53+
54+
var deprecatedProducts = []*model.DeprecatedProduct{
55+
{
56+
Sku: "apollo-federation-v1",
57+
Package: "@apollo/federation-v1",
58+
Reason: &deprecationReason,
59+
},
60+
}
61+
62+
var federationStudyDescription = "Federation Study"
63+
var studioStudyDescription = "Studio Study"
64+
65+
var research = []*model.ProductResearch{
66+
{
67+
Study: &model.CaseStudy{
68+
CaseNumber: "1234",
69+
Description: &federationStudyDescription,
70+
},
71+
},
72+
{
73+
Study: &model.CaseStudy{
74+
CaseNumber: "1235",
75+
Description: &studioStudyDescription,
76+
},
77+
},
78+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package resolver
2+
3+
import (
4+
"math"
5+
6+
"graphql-go-compatibility/model"
7+
)
8+
9+
func FindDeprecatedProductBySkuAndPackage(sku string, packageArg string) (*model.DeprecatedProduct, error) {
10+
for i := range deprecatedProducts {
11+
if deprecatedProducts[i].Sku == sku && deprecatedProducts[i].Package == packageArg {
12+
return deprecatedProducts[i], nil
13+
}
14+
}
15+
return nil, nil
16+
}
17+
18+
func FindProductById(id string) (*model.Product, error) {
19+
for i := range products {
20+
if products[i].ID == id {
21+
return products[i], nil
22+
}
23+
}
24+
return nil, nil
25+
}
26+
27+
func FindProductBySkuAndPackage(sku string, packageArg string) (*model.Product, error) {
28+
for i := range products {
29+
if *products[i].Sku == sku && *products[i].Package == packageArg {
30+
return products[i], nil
31+
}
32+
}
33+
return nil, nil
34+
}
35+
36+
func FindProductBySkuAndVariationID(sku string, variationID string) (*model.Product, error) {
37+
for i := range products {
38+
if *products[i].Sku == sku && products[i].Variation.ID == variationID {
39+
return products[i], nil
40+
}
41+
}
42+
return nil, nil
43+
}
44+
45+
func FindProductResearchByProductId(product *model.Product) ([]*model.ProductResearch, error) {
46+
switch product.ID {
47+
case "apollo-federation":
48+
return research[:1], nil
49+
case "apollo-studio":
50+
return research[1:], nil
51+
default:
52+
return nil, nil
53+
}
54+
}
55+
56+
func CalculateAverageProductsCreatedPerYear(user *model.User) (*int, error) {
57+
if user.TotalProductsCreated == nil {
58+
return nil, nil
59+
}
60+
var avgProductsCreated = int(math.Round(float64(*user.TotalProductsCreated) / float64(user.YearsOfEmployment)))
61+
return &avgProductsCreated, nil
62+
}
63+
64+
// entity resolvers
65+
66+
// @key(fields: "sku package")
67+
func DeprecatedProductEntityResolver(params map[string]interface{}) (*model.DeprecatedProduct, error) {
68+
sku, skuOk := params["sku"].(string)
69+
pkg, pkgOk := params["package"].(string)
70+
if skuOk && pkgOk {
71+
return FindDeprecatedProductBySkuAndPackage(sku, pkg)
72+
}
73+
return nil, nil
74+
}
75+
76+
// @key(fields: "id")
77+
// @key(fields: "sku package")
78+
// @key(fields: "sku variation { id }")
79+
func ProductEntityResolver(params map[string]interface{}) (*model.Product, error) {
80+
id, ok := params["id"].(string)
81+
if ok {
82+
return FindProductById(id)
83+
} else {
84+
sku, skuOk := params["sku"].(string)
85+
if skuOk {
86+
pkg, pkgOk := params["package"].(string)
87+
if pkgOk {
88+
return FindProductBySkuAndPackage(sku, pkg)
89+
} else {
90+
variation, varOk := params["variation"].(map[string]interface{})
91+
if varOk {
92+
varId, varIdOk := variation["id"].(string)
93+
if varIdOk {
94+
return FindProductBySkuAndVariationID(sku, varId)
95+
}
96+
}
97+
}
98+
}
99+
}
100+
return nil, nil
101+
}
102+
103+
// @key(fields: "study { caseNumber }")
104+
func ProductResearchEntityResolver(params map[string]interface{}) (*model.ProductResearch, error) {
105+
study, ok := params["study"].(map[string]interface{})
106+
if ok {
107+
caseNumber, caseOk := study["caseNumber"].(string)
108+
if caseOk {
109+
for i := range research {
110+
if research[i].Study.CaseNumber == caseNumber {
111+
return research[i], nil
112+
}
113+
}
114+
}
115+
}
116+
return nil, nil
117+
}
118+
119+
// @key(fields: "email")
120+
func UserEntityResolver(params map[string]interface{}) (*model.User, error) {
121+
email, ok := params["email"].(string)
122+
if ok {
123+
for i := range users {
124+
var user *model.User
125+
if users[i].Email == email {
126+
user = users[i]
127+
}
128+
129+
totalProducts, totalSpecified := params["totalProductsCreated"].(float64)
130+
if totalSpecified {
131+
total := int(totalProducts)
132+
user.TotalProductsCreated = &total
133+
}
134+
135+
yearsOfEmployment, yearsSpecified := params["yearsOfEmployment"].(float64)
136+
if yearsSpecified {
137+
user.YearsOfEmployment = int(yearsOfEmployment)
138+
}
139+
140+
if user != nil {
141+
return user, nil
142+
}
143+
}
144+
}
145+
return nil, nil
146+
}

0 commit comments

Comments
 (0)