Skip to content

Commit

Permalink
fix: Add __typename support (#871)
Browse files Browse the repository at this point in the history
As per GQL spec
  • Loading branch information
AndrewSisley authored Oct 7, 2022
1 parent f9747f1 commit 398fe03
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 8 deletions.
32 changes: 31 additions & 1 deletion core/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Package core provides commonly shared interfaces and building blocks.
*/
package core

import (
"github.com/sourcenetwork/defradb/client"
parserTypes "github.com/sourcenetwork/defradb/query/graphql/parser/types"
)

const DocKeyFieldIndex int = 0

type DocFields []any
Expand Down Expand Up @@ -71,7 +76,18 @@ type RenderKey struct {
Key string
}

type mappingTypeInfo struct {
// The index at which the type name is to be held
Index int

// The name of the host type
Name string
}

type DocumentMapping struct {
// The type information for the object, if provided.
typeInfo client.Option[mappingTypeInfo]

// The set of fields that should be rendered.
//
// Fields not in this collection will not be rendered to the consumer.
Expand Down Expand Up @@ -109,6 +125,7 @@ func NewDocumentMapping() *DocumentMapping {
// CloneWithoutRender deep copies the source mapping skipping over the RenderKeys.
func (source *DocumentMapping) CloneWithoutRender() *DocumentMapping {
result := DocumentMapping{
typeInfo: source.typeInfo,
IndexesByName: make(map[string][]int, len(source.IndexesByName)),
nextIndex: source.nextIndex,
ChildMappings: make([]*DocumentMapping, len(source.ChildMappings)),
Expand Down Expand Up @@ -188,7 +205,11 @@ func (mapping *DocumentMapping) ToMap(doc Doc) map[string]any {
innerMapping := mapping.ChildMappings[renderKey.Index]
renderValue = innerMapping.ToMap(innerV)
default:
renderValue = innerV
if mapping.typeInfo.HasValue() && renderKey.Index == mapping.typeInfo.Value().Index {
renderValue = mapping.typeInfo.Value().Name
} else {
renderValue = innerV
}
}
mappedDoc[renderKey.Key] = renderValue
}
Expand All @@ -206,6 +227,15 @@ func (mapping *DocumentMapping) Add(index int, name string) {
}
}

func (mapping *DocumentMapping) SetTypeName(typeName string) {
index := mapping.GetNextIndex()
mapping.Add(index, parserTypes.TypeNameFieldName)
mapping.typeInfo = client.Some(mappingTypeInfo{
Index: index,
Name: typeName,
})
}

// SetChildAt sets the given child mapping at the given index.
//
// If the index is greater than the ChildMappings length the collection will
Expand Down
8 changes: 8 additions & 0 deletions query/graphql/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,22 @@ func getTopLevelInfo(
mapping.Add(int(f.ID), f.Name)
}

// Setting the type name must be done after adding the fields, as
// the typeName index is dynamic, but the field indexes are not
mapping.SetTypeName(collectionName)

return mapping, &desc, nil
}

if parsed.Name == parserTypes.LinksFieldName {
mapping.SetTypeName(parserTypes.LinksFieldName)

for f := range parserTypes.LinksFields {
mapping.Add(mapping.GetNextIndex(), f)
}
} else {
mapping.SetTypeName(parserTypes.CommitTypeName)

for f := range parserTypes.VersionFields {
mapping.Add(mapping.GetNextIndex(), f)
}
Expand Down
18 changes: 12 additions & 6 deletions query/graphql/parser/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ type (
)

const (
// GQL special field, returns the host object's type name
// https://spec.graphql.org/October2021/#sec-Type-Name-Introspection
TypeNameFieldName = "__typename"

Cid = "cid"
Data = "data"
DocKey = "dockey"
Expand All @@ -79,6 +83,7 @@ const (

ExplainLabel = "explain"

CommitTypeName = "Commit"
LinksFieldName = "links"
HeightFieldName = "height"
CidFieldName = "cid"
Expand Down Expand Up @@ -109,12 +114,13 @@ var (
}

ReservedFields = map[string]bool{
VersionFieldName: true,
GroupFieldName: true,
CountFieldName: true,
SumFieldName: true,
AverageFieldName: true,
DocKeyFieldName: true,
TypeNameFieldName: true,
VersionFieldName: true,
GroupFieldName: true,
CountFieldName: true,
SumFieldName: true,
AverageFieldName: true,
DocKeyFieldName: true,
}

Aggregates = map[string]struct{}{
Expand Down
2 changes: 1 addition & 1 deletion query/graphql/schema/types/commits.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
// Any self referential type needs to be initalized
// inside the init() func
CommitObject = gql.NewObject(gql.ObjectConfig{
Name: "Commit",
Name: parserTypes.CommitTypeName,
Fields: gql.Fields{
"height": &gql.Field{
Type: gql.Int,
Expand Down
53 changes: 53 additions & 0 deletions tests/integration/query/all_commits/with_dockey_typename_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package all_commits

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration"
)

func TestQueryAllCommitsWithDockeyWithTypeName(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple all commits query with dockey and typename",
Query: `query {
allCommits(dockey: "bae-52b9170d-b77a-5887-b877-cbdbb99b009f") {
cid
__typename
}
}`,
Docs: map[int][]string{
0: {
`{
"Name": "John",
"Age": 21
}`,
},
},
Results: []map[string]any{
{
"cid": "bafybeidst2mzxhdoh4ayjdjoh4vibo7vwnuoxk3xgyk5mzmep55jklni2a",
"__typename": "Commit",
},
{
"cid": "bafybeihhypcsqt7blkrqtcmpl43eo3yunrog5pchox5naji6hisdme4swm",
"__typename": "Commit",
},
{
"cid": "bafybeid57gpbwi4i6bg7g357vwwyzsmr4bjo22rmhoxrwqvdxlqxcgaqvu",
"__typename": "Commit",
},
},
}

executeTestCase(t, test)
}
63 changes: 63 additions & 0 deletions tests/integration/query/one_to_many/with_typename_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package one_to_many

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration"
)

func TestQueryOneToManyWithTypeName(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "One-to-many relation query from one side with typename",
Query: `query {
book {
name
__typename
author {
name
__typename
}
}
}`,
Docs: map[int][]string{
//books
0: { // bae-fd541c25-229e-5280-b44b-e5c2af3e374d
`{
"name": "Painted House",
"rating": 4.9,
"author_id": "bae-41598f0c-19bc-5da6-813b-e80f14a10df3"
}`,
},
//authors
1: { // bae-41598f0c-19bc-5da6-813b-e80f14a10df3
`{
"name": "John Grisham",
"age": 65,
"verified": true
}`,
},
},
Results: []map[string]any{
{
"name": "Painted House",
"__typename": "book",
"author": map[string]any{
"name": "John Grisham",
"__typename": "author",
},
},
},
}

executeTestCase(t, test)
}
77 changes: 77 additions & 0 deletions tests/integration/query/simple/with_group_typename_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package simple

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration"
)

func TestQuerySimpleWithGroupByWithTypeName(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query group by and parent typename",
Query: `query {
users(groupBy: [Name]) {
Name
__typename
}
}`,
Docs: map[int][]string{
0: {
`{
"Name": "John"
}`,
},
},
Results: []map[string]any{
{
"Name": "John",
"__typename": "users",
},
},
}

executeTestCase(t, test)
}

func TestQuerySimpleWithGroupByWithChildTypeName(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query group by and child typename",
Query: `query {
users(groupBy: [Name]) {
Name
_group {
__typename
}
}
}`,
Docs: map[int][]string{
0: {
`{
"Name": "John"
}`,
},
},
Results: []map[string]any{
{
"Name": "John",
"_group": []map[string]any{
{
"__typename": "users",
},
},
},
},
}

executeTestCase(t, test)
}
Loading

0 comments on commit 398fe03

Please sign in to comment.