diff --git a/examples/endpoints/connection_test.go b/examples/endpoints/connection_test.go new file mode 100644 index 0000000..a4a2d0f --- /dev/null +++ b/examples/endpoints/connection_test.go @@ -0,0 +1,250 @@ +package endpoints_test + +import ( + "reflect" + "testing" + + "github.com/graphql-go/graphql" + "github.com/graphql-go/graphql/testutil" + "github.com/graphql-go/relay/examples/endpoints" +) + +func TestConnection_TestFetching_CorrectlyFetchesTheFirstShipOfThesites(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 1) { + edges { + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyFetchesTheFirstTwoendpointsOfThesitesWithACursor(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 2) { + edges { + cursor, + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjA=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjE=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339u", + }, + }, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyFetchesTheNextTwoEndpointsOfThesitesWithACursor(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 2, after: "YXJyYXljb25uZWN0aW9uOjA=") { + edges { + cursor, + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjE=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339u", + }, + }, + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjI=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4338u", + }, + }, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyFetchesNoendpointsOfThesitesAtTheEndOfTheConnection(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 2, after: "YXJyYXljb25uZWN0aW9uOjQ=") { + edges { + cursor, + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{}, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyIdentifiesTheEndOfTheList(t *testing.T) { + query := ` + query { + sites { + siteId, + originalendpoints: endpoints(first: 2) { + edges { + node { + endpointId + } + } + pageInfo { + hasNextPage + } + } + moreendpoints: endpoints(first: 1 after: "YXJyYXljb25uZWN0aW9uOjE=") { + edges { + node { + endpointId + } + } + pageInfo { + hasNextPage + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "originalendpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339u", + }, + }, + }, + "pageInfo": map[string]interface{}{ + "hasNextPage": true, + }, + }, + "moreendpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4338u", + }, + }, + }, + "pageInfo": map[string]interface{}{ + "hasNextPage": false, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} diff --git a/examples/endpoints/data.go b/examples/endpoints/data.go new file mode 100644 index 0000000..66b4643 --- /dev/null +++ b/examples/endpoints/data.go @@ -0,0 +1,62 @@ +package endpoints + +/* +This defines a basic set of data for our Star Wars Schema. + +This data is hard coded for the sake of the demo, but you could imagine +fetching this data from a backend service rather than from hardcoded +JSON objects in a more complex demo. +*/ + +//Endpoint ... +type Endpoint struct { + ClientID string `json:"clientId"` + SiteID string `json:"siteId"` + EndpointID string `json:"endpointId"` +} + +//Site ... +type Site struct { + ClientID string `json:"clientId"` + SiteID string `json:"siteId"` + Code string `json:"siteCode"` + Name string `json:"siteName"` + Endpoints []Endpoint `json:"endpoints"` +} + +var E1 = &Endpoint{"6500023", "6500023", "37866524-cc91-4d64-b5db-b912eaf4339e"} +var E2 = &Endpoint{"6500023", "6500023", "37866524-cc91-4d64-b5db-b912eaf4339u"} +var E3 = &Endpoint{"6500023", "6500023", "37866524-cc91-4d64-b5db-b912eaf4338u"} + +var S1 = &Site{ + "6500023", + "6500023", + "test", + "test-name", + []Endpoint{*E1, *E2, *E3}, +} + +var endpointss = map[string]*Endpoint{ + "37866524-cc91-4d64-b5db-b912eaf4339e": E1, + "37866524-cc91-4d64-b5db-b912eaf4339u": E2, + "37866524-cc91-4d64-b5db-b912eaf4338u": E3, +} +var sites = map[string]*Site{ + "6500023": S1, +} + +func GetSite(id string) *Site { + if site, ok := sites[id]; ok { + return site + } + return nil +} +func GetEndpoint(id string) *Endpoint { + if endpoint, ok := endpointss[id]; ok { + return endpoint + } + return nil +} +func GetSites() *Site { + return S1 +} diff --git a/examples/endpoints/object_identification_test.go b/examples/endpoints/object_identification_test.go new file mode 100644 index 0000000..0972fba --- /dev/null +++ b/examples/endpoints/object_identification_test.go @@ -0,0 +1,91 @@ +package endpoints_test + +import ( + "reflect" + "testing" + + "github.com/graphql-go/graphql" + "github.com/graphql-go/graphql/testutil" + "github.com/graphql-go/relay/examples/endpoints" +) + +func TestObjectIdentification_TestFetching_CorrectlyFetchesTheIDAndTheNameOfThesites(t *testing.T) { + query := ` + query sitesQuery { + sites { + id + siteId + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "id": "U2l0ZTo2NTAwMDIz", + "siteId": "6500023", + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestObjectIdentification_TestFetching_CorrectlyRefetchesThesites(t *testing.T) { + query := ` + query sitesRefetchQuery { + node(id: "U2l0ZTo2NTAwMDIz") { + id + ... on Site { + siteId + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "node": map[string]interface{}{ + "id": "U2l0ZTo2NTAwMDIz", + "siteId": "6500023", + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} + +func TestObjectIdentification_TestFetching_CorrectlyRefetchesTheEndpoint(t *testing.T) { + query := ` + query { + node(id: "RW5kcG9pbnQ6Mzc4NjY1MjQtY2M5MS00ZDY0LWI1ZGItYjkxMmVhZjQzMzll") { + id + ... on Endpoint { + endpointId + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "node": map[string]interface{}{ + "id": "RW5kcG9pbnQ6Mzc4NjY1MjQtY2M5MS00ZDY0LWI1ZGItYjkxMmVhZjQzMzll", + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpoints.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} diff --git a/examples/endpoints/schema.go b/examples/endpoints/schema.go new file mode 100644 index 0000000..f490544 --- /dev/null +++ b/examples/endpoints/schema.go @@ -0,0 +1,159 @@ +package endpoints + +import ( + "errors" + + "github.com/graphql-go/graphql" + "github.com/graphql-go/relay" + "golang.org/x/net/context" +) + +var nodeDefinitions *relay.NodeDefinitions +var siteType *graphql.Object +var endpointType *graphql.Object + +// exported schema, defined in init() +var Schema graphql.Schema + +func siteIDFetcher(obj interface{}, info graphql.ResolveInfo, ctx context.Context) (string, error) { + if s, ok := obj.(*Site); ok { + return s.SiteID, nil + } + return "", errors.New("Not valid type") +} + +func endpointIDFetcher(obj interface{}, info graphql.ResolveInfo, ctx context.Context) (string, error) { + if s, ok := obj.(Endpoint); ok { + return s.EndpointID, nil + } + if s, ok := obj.(*Endpoint); ok { + return s.EndpointID, nil + } + return "", errors.New("Not valid type") +} + +func init() { + + /**s + * We get the node interface and field from the relay library. + * + * The first method is the way we resolve an ID to its object. The second is the + * way we resolve an object that implements node to its type. + */ + nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ + IDFetcher: func(id string, info graphql.ResolveInfo, ctx context.Context) (interface{}, error) { + // resolve id from global id + resolvedID := relay.FromGlobalID(id) + + // based on id and its type, return the object + switch resolvedID.Type { + case "Site": + return GetSite(resolvedID.ID), nil + case "Endpoint": + return GetEndpoint(resolvedID.ID), nil + default: + return nil, errors.New("Unknown node type") + } + }, + TypeResolve: func(p graphql.ResolveTypeParams) *graphql.Object { + // based on the type of the value, return GraphQLObjectType + switch p.Value.(type) { + case *Site: + return siteType + default: + return endpointType + } + }, + }) + + endpointType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Endpoint", + Description: "endpoint", + Fields: graphql.Fields{ + "id": relay.GlobalIDField("Endpoint", endpointIDFetcher), + "clientId": &graphql.Field{ + Type: graphql.String, + }, + "siteId": &graphql.Field{ + Type: graphql.String, + }, + "endpointId": &graphql.Field{ + Type: graphql.String, + }, + }, + Interfaces: []*graphql.Interface{ + nodeDefinitions.NodeInterface, + }, + }) + + endpointConnectionDefinition := relay.ConnectionDefinitions(relay.ConnectionConfig{ + Name: "Endpoint", + NodeType: endpointType, + }) + + siteType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Site", + Description: "site", + Fields: graphql.Fields{ + "id": relay.GlobalIDField("Site", siteIDFetcher), + "clientId": &graphql.Field{ + Type: graphql.String, + }, + "siteId": &graphql.Field{ + Type: graphql.String, + }, + "siteCode": &graphql.Field{ + Type: graphql.String, + }, + "siteName": &graphql.Field{ + Type: graphql.String, + }, + "endpoints": &graphql.Field{ + Type: endpointConnectionDefinition.ConnectionType, + Args: relay.ConnectionArgs, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + // convert args map[string]interface into ConnectionArguments + args := relay.NewConnectionArguments(p.Args) + + endpoints := []interface{}{} + if site, ok := p.Source.(*Site); ok { + for _, e := range site.Endpoints { + endpoints = append(endpoints, e) + } + } + return relay.ConnectionFromArray(endpoints, args), nil + }, + }, + }, + Interfaces: []*graphql.Interface{ + nodeDefinitions.NodeInterface, + }, + }) + + queryType := graphql.NewObject(graphql.ObjectConfig{ + Name: "Query", + Fields: graphql.Fields{ + "sites": &graphql.Field{ + Type: siteType, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + return GetSites(), nil + }, + }, + "node": nodeDefinitions.NodeField, + }, + }) + + /** + * Finally, we construct our schema (whose starting query type is the query + * type we defined above) and export it. + */ + var err error + Schema, err = graphql.NewSchema(graphql.SchemaConfig{ + Query: queryType, + //Mutation: mutationType, + }) + if err != nil { + // panic if there is an error in schema + panic(err) + } +} diff --git a/examples/endpointsdynamic/connection_test.go b/examples/endpointsdynamic/connection_test.go new file mode 100644 index 0000000..34e6fbe --- /dev/null +++ b/examples/endpointsdynamic/connection_test.go @@ -0,0 +1,254 @@ +package endpointsdynamic_test + +import ( + "reflect" + "testing" + + "github.com/graphql-go/graphql" + "github.com/graphql-go/graphql/testutil" + "github.com/graphql-go/relay/examples/endpointsdynamic" +) + +func init() { + endpointsdynamic.UpdateSchema() +} + +func TestConnection_TestFetching_CorrectlyFetchesTheFirstShipOfThesites(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 1) { + edges { + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyFetchesTheFirstTwoendpointsOfThesitesWithACursor(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 2) { + edges { + cursor, + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjA=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjE=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339u", + }, + }, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyFetchesTheNextTwoEndpointsOfThesitesWithACursor(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 2, after: "YXJyYXljb25uZWN0aW9uOjA=") { + edges { + cursor, + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjE=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339u", + }, + }, + map[string]interface{}{ + "cursor": "YXJyYXljb25uZWN0aW9uOjI=", + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4338u", + }, + }, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyFetchesNoendpointsOfThesitesAtTheEndOfTheConnection(t *testing.T) { + query := ` + query { + sites { + siteId, + endpoints(first: 2, after: "YXJyYXljb25uZWN0aW9uOjQ=") { + edges { + cursor, + node { + endpointId + } + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "endpoints": map[string]interface{}{ + "edges": []interface{}{}, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} +func TestConnection_TestFetching_CorrectlyIdentifiesTheEndOfTheList(t *testing.T) { + query := ` + query { + sites { + siteId, + originalendpoints: endpoints(first: 2) { + edges { + node { + endpointId + } + } + pageInfo { + hasNextPage + } + } + moreendpoints: endpoints(first: 1 after: "YXJyYXljb25uZWN0aW9uOjE=") { + edges { + node { + endpointId + } + } + pageInfo { + hasNextPage + } + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "siteId": "6500023", + "originalendpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339u", + }, + }, + }, + "pageInfo": map[string]interface{}{ + "hasNextPage": true, + }, + }, + "moreendpoints": map[string]interface{}{ + "edges": []interface{}{ + map[string]interface{}{ + "node": map[string]interface{}{ + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4338u", + }, + }, + }, + "pageInfo": map[string]interface{}{ + "hasNextPage": false, + }, + }, + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} diff --git a/examples/endpointsdynamic/data.go b/examples/endpointsdynamic/data.go new file mode 100644 index 0000000..e34c170 --- /dev/null +++ b/examples/endpointsdynamic/data.go @@ -0,0 +1,69 @@ +package endpointsdynamic + +/* +This defines a basic set of data for our Star Wars Schema. + +This data is hard coded for the sake of the demo, but you could imagine +fetching this data from a backend service rather than from hardcoded +JSON objects in a more complex demo. +*/ + +//Endpoint ... +type Endpoint struct { + ClientID string `json:"clientId"` + SiteID string `json:"siteId"` + EndpointID string `json:"endpointId"` +} + +//Site ... +type Site struct { + ClientID string `json:"clientId"` + SiteID string `json:"siteId"` + Code string `json:"siteCode"` + Name string `json:"siteName"` + Endpoints []Endpoint `json:"endpoints"` +} + +var E1 = &Endpoint{"6500023", "6500023", "37866524-cc91-4d64-b5db-b912eaf4339e"} +var E2 = &Endpoint{"6500023", "6500023", "37866524-cc91-4d64-b5db-b912eaf4339u"} +var E3 = &Endpoint{"6500023", "6500023", "37866524-cc91-4d64-b5db-b912eaf4338u"} + +var S1 = &Site{ + "6500023", + "6500023", + "test", + "test-name", + []Endpoint{*E1, *E2, *E3}, +} + +var endpoints = map[string]*Endpoint{ + "37866524-cc91-4d64-b5db-b912eaf4339e": E1, + "37866524-cc91-4d64-b5db-b912eaf4339u": E2, + "37866524-cc91-4d64-b5db-b912eaf4338u": E3, +} +var sites = map[string]*Site{ + "6500023": S1, +} + +func GetSite(id string) interface{} { + if site, ok := sites[id]; ok { + return site + } + return nil +} +func GetEndpoint(id string) interface{} { + if endpoint, ok := endpoints[id]; ok { + return endpoint + } + return nil +} + +// dummy fetcher +func GetSites() *Site { + return S1 +} + +// dummy fetcher +func GetEndpoints() *Endpoint { + return E1 +} diff --git a/examples/endpointsdynamic/object_identification_test.go b/examples/endpointsdynamic/object_identification_test.go new file mode 100644 index 0000000..568b705 --- /dev/null +++ b/examples/endpointsdynamic/object_identification_test.go @@ -0,0 +1,68 @@ +package endpointsdynamic_test + +import ( + "reflect" + "testing" + + "github.com/graphql-go/graphql" + "github.com/graphql-go/graphql/testutil" + "github.com/graphql-go/relay/examples/endpointsdynamic" +) + +func init() { + endpointsdynamic.UpdateSchema() +} + +func TestObjectIdentification_TestFetching_CorrectlyFetchesTheIDAndTheNameOfThesites(t *testing.T) { + query := ` + query sitesQuery { + sites { + id + siteId + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "sites": map[string]interface{}{ + "id": "U2l0ZTo2NTAwMDIz", + "siteId": "6500023", + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} + +func TestObjectIdentification_TestFetching_CorrectlyRefetchesTheEndpoint(t *testing.T) { + query := ` + query { + node(id: "RW5kcG9pbnQ6Mzc4NjY1MjQtY2M5MS00ZDY0LWI1ZGItYjkxMmVhZjQzMzll") { + id + ... on Endpoint { + endpointId + } + } + } + ` + expected := &graphql.Result{ + Data: map[string]interface{}{ + "node": map[string]interface{}{ + "id": "RW5kcG9pbnQ6Mzc4NjY1MjQtY2M5MS00ZDY0LWI1ZGItYjkxMmVhZjQzMzll", + "endpointId": "37866524-cc91-4d64-b5db-b912eaf4339e", + }, + }, + } + result := graphql.Do(graphql.Params{ + Schema: endpointsdynamic.Schema, + RequestString: query, + }) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result)) + } +} diff --git a/examples/endpointsdynamic/schema.go b/examples/endpointsdynamic/schema.go new file mode 100644 index 0000000..383c8ec --- /dev/null +++ b/examples/endpointsdynamic/schema.go @@ -0,0 +1,176 @@ +package endpointsdynamic + +import ( + "context" + "errors" + + "github.com/graphql-go/graphql" + "github.com/graphql-go/relay" +) + +var nodeDefinitions *relay.NodeDefinitions +var siteType *graphql.Object +var endpointType *graphql.Object +var queryType *graphql.Object + +var resolverMap = map[string]func(string) interface{}{ + "Site": GetSite, + "Endpoint": GetEndpoint, +} + +func siteIDFetcher(obj interface{}, info graphql.ResolveInfo, ctx context.Context) (string, error) { + if s, ok := obj.(*Site); ok { + return s.SiteID, nil + } + return "", errors.New("Not valid type") +} + +func endpointIDFetcher(obj interface{}, info graphql.ResolveInfo, ctx context.Context) (string, error) { + if s, ok := obj.(Endpoint); ok { + return s.EndpointID, nil + } + if s, ok := obj.(*Endpoint); ok { + return s.EndpointID, nil + } + return "", errors.New("Not valid type") +} + +// Schema is exported, defined in init() +var Schema graphql.Schema + +func init() { + + siteType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Site", + Description: "site", + IsTypeOf: func(p graphql.IsTypeOfParams) bool { + _, ok := p.Value.(*Site) + return ok + }, + Fields: graphql.Fields{ + "id": relay.GlobalIDField("Site", siteIDFetcher), + "clientId": &graphql.Field{ + Type: graphql.String, + }, + "siteId": &graphql.Field{ + Type: graphql.String, + }, + "siteCode": &graphql.Field{ + Type: graphql.String, + }, + "siteName": &graphql.Field{ + Type: graphql.String, + }, + }, + }) + + queryType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Query", + Fields: graphql.Fields{ + "sites": &graphql.Field{ + Type: siteType, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + return GetSites(), nil + }, + }, + }, + }) + + /** + * Finally, we construct our schema (whose starting query type is the query + * type we defined above) and export it. + */ + var err error + Schema, err = graphql.NewSchema(graphql.SchemaConfig{ + Query: queryType, + //Mutation: mutationType, + }) + if err != nil { + // panic if there is an error in schema + panic(err) + } +} + +// UpdateSchema with dynamic Endpoint type. Site fetch need to paginate endpoints +func UpdateSchema() { + + /** + * We get the node interface and field from the relay library. + * + * The first method is the way we resolve an ID to its object. The second is the + * way we resolve an object that implements node to its type. + */ + nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ + IDFetcher: func(id string, info graphql.ResolveInfo, ctx context.Context) (interface{}, error) { + // resolve id from global id + resolvedID := relay.FromGlobalID(id) + if f, ok := resolverMap[resolvedID.Type]; ok { + return f(resolvedID.ID), nil + } + return nil, errors.New("Unknown node type") + }, + }) + + endpointType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Endpoint", + Description: "endpoint", + IsTypeOf: func(p graphql.IsTypeOfParams) bool { + _, ok := p.Value.(Endpoint) + _, ok2 := p.Value.(*Endpoint) + return ok || ok2 + }, + Fields: graphql.Fields{ + "id": relay.GlobalIDField("Endpoint", endpointIDFetcher), + "clientId": &graphql.Field{ + Type: graphql.String, + }, + "siteId": &graphql.Field{ + Type: graphql.String, + }, + "endpointId": &graphql.Field{ + Type: graphql.String, + }, + }, + Interfaces: []*graphql.Interface{ + nodeDefinitions.NodeInterface, + }, + }) + + endpointConnectionDefinition := relay.ConnectionDefinitions(relay.ConnectionConfig{ + Name: "Endpoint", + NodeType: endpointType, + }) + + siteType.AddFieldConfig("endpoints", &graphql.Field{ + Type: endpointConnectionDefinition.ConnectionType, + Args: relay.ConnectionArgs, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + // convert args map[string]interface into ConnectionArguments + args := relay.NewConnectionArguments(p.Args) + + endpoints := []interface{}{} + if site, ok := p.Source.(*Site); ok { + for _, e := range site.Endpoints { + endpoints = append(endpoints, e) + } + } + return relay.ConnectionFromArray(endpoints, args), nil + }, + }) + queryType.AddFieldConfig("endpoints", &graphql.Field{ + Type: endpointType, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + return GetEndpoints(), nil + }, + }) + queryType.AddFieldConfig("node", nodeDefinitions.NodeField) + + var err error + Schema, err = graphql.NewSchema(graphql.SchemaConfig{ + Query: queryType, + }) + if err != nil { + // panic if there is an error in schema + panic(err) + } +}