Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/planning interfaces with entity implementations #624

Merged
merged 45 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4e534b3
fix planning interface object
devsergiy Sep 29, 2023
978ce82
add explicit null to test
devsergiy Sep 29, 2023
988eab2
fix: representation variables should be nullable objects
devsergiy Sep 29, 2023
7ebfcfb
chore: move fetch configuration to resolver
devsergiy Sep 29, 2023
d529873
embed fetch configuration into SingleFetch
devsergiy Sep 29, 2023
ed1658b
rename BatchFetch to EntityBatchFetch
devsergiy Sep 29, 2023
9116c47
add dissalow single flight to entity fetches
devsergiy Sep 29, 2023
e2eae65
take into consideratio disable single flight from fetch
devsergiy Sep 29, 2023
8fdde26
add create entity fetch in postprocess
devsergiy Sep 29, 2023
eee3aec
add resolve entity fetch to loader
devsergiy Sep 29, 2023
dce2865
always add onType to representation variable
devsergiy Sep 29, 2023
06565ae
chore: fix compilation errors
devsergiy Oct 2, 2023
e5a588c
fix representations variables in federation tests
devsergiy Oct 2, 2023
43d59bc
fix obtaining field type name in addPlannerPathForUnionChildOfObjectP…
devsergiy Oct 2, 2023
e58d89b
add separate selection set rewriter
devsergiy Oct 3, 2023
bf4510e
add more ast helpers
devsergiy Oct 3, 2023
6bfabaf
add need rewrite rules
devsergiy Oct 3, 2023
9df6f08
draft
devsergiy Oct 4, 2023
d0b36fb
cleanup
devsergiy Oct 11, 2023
5f3e6e3
fix ref usage
devsergiy Oct 11, 2023
e5e614e
add helpers to create and update inline fragments
devsergiy Oct 11, 2023
1e9298a
finish draft
devsergiy Oct 11, 2023
6a824b0
add initial test
devsergiy Oct 11, 2023
eec1d72
move helper to ast
devsergiy Oct 12, 2023
e98e68e
fix rewrite during planning
devsergiy Oct 13, 2023
e35003b
add more test cases
devsergiy Oct 15, 2023
0bb079c
add more test cases to interface selection rewriter
devsergiy Oct 16, 2023
966742b
fix expanding on a root field
devsergiy Oct 16, 2023
c13175b
add interface expanding on array of interfaces test case for graphql …
devsergiy Oct 16, 2023
78d1e6b
fix preserving requested __typename
devsergiy Oct 16, 2023
1aead8c
add option to enable/disable refs in debug print of operation
devsergiy Oct 18, 2023
efe9186
refactor interface rewriter to support unions rewrite
devsergiy Oct 19, 2023
ba41e08
remove on type fragments with types not exists in the current datasource
devsergiy Oct 23, 2023
3588a33
Merge remote-tracking branch 'origin/master' into fix/planning-interf…
devsergiy Oct 23, 2023
c1dfd04
fix tests after merge
devsergiy Oct 23, 2023
3989b00
add union & interface test cases
devsergiy Oct 23, 2023
a8e6771
implement need rewrite check for unions
devsergiy Oct 24, 2023
6c8ff13
implement union selection rewriter
devsergiy Oct 24, 2023
b571a1f
fallback to __typename field selection for union when no inline fragm…
devsergiy Oct 24, 2023
ad2e00f
implement fallback to __typename field selection for interface when n…
devsergiy Oct 24, 2023
23a1ee3
use upstream schema to get information about interface or union type …
devsergiy Oct 25, 2023
f2da2c5
fix imports
devsergiy Oct 25, 2023
fdce7a8
fix graphql datasource federation test
devsergiy Oct 25, 2023
103ef0a
fix execution test
devsergiy Oct 25, 2023
3f24929
fix gosimple linter
devsergiy Oct 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions v2/pkg/ast/ast_field.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,10 @@ func (d *Document) FieldsAreEqualFlat(left, right int) bool {
d.ArgumentSetsAreEquals(d.FieldArguments(left), d.FieldArguments(right)) && // arguments
d.DirectiveSetsAreEqual(d.FieldDirectives(left), d.FieldDirectives(right)) // directives
}

func (d *Document) FieldSelectionSet(ref int) (selectionSetRef int, ok bool) {
if !d.Fields[ref].HasSelections {
return InvalidRef, false
}
return d.Fields[ref].SelectionSet, true
}
4 changes: 4 additions & 0 deletions v2/pkg/ast/ast_field_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ func (d *Document) FieldDefinitionTypeNameString(ref int) string {
return d.ResolveTypeNameString(d.FieldDefinitions[ref].Type)
}

func (d *Document) FieldDefinitionTypeNameBytes(ref int) ByteSlice {
return d.ResolveTypeNameBytes(d.FieldDefinitions[ref].Type)
}

func (d *Document) FieldDefinitionTypeNode(ref int) Node {
typeName := d.ResolveTypeNameBytes(d.FieldDefinitions[ref].Type)
node, _ := d.Index.FirstNodeByNameBytes(typeName)
Expand Down
7 changes: 7 additions & 0 deletions v2/pkg/ast/ast_inline_fragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,10 @@ func (d *Document) AddInlineFragment(fragment InlineFragment) int {
func (d *Document) InlineFragmentIsOfTheSameType(ref int) bool {
return d.InlineFragments[ref].IsOfTheSameType
}

func (d *Document) InlineFragmentSelectionSet(ref int) (selectionSetRef int, ok bool) {
if !d.InlineFragments[ref].HasSelections {
return InvalidRef, false
}
return d.InlineFragments[ref].SelectionSet, true
}
20 changes: 20 additions & 0 deletions v2/pkg/ast/ast_interface_type_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,23 @@ func (d *Document) InterfaceTypeDefinitionFieldWithName(ref int, fieldName []byt
}
return InvalidRef, false
}

func (d *Document) InterfaceTypeDefinitionImplementedByObjectWithNames(interfaceDefRef int) (typeNames []string, ok bool) {
implementedByNodes := d.InterfaceTypeDefinitionImplementedByRootNodes(interfaceDefRef)

typeNames = make([]string, 0, len(implementedByNodes))
for _, implementedByNode := range implementedByNodes {
if implementedByNode.Kind != NodeKindObjectTypeDefinition {
continue
}

typeNames = append(typeNames, implementedByNode.NameString(d))
}

if len(typeNames) > 0 {
return typeNames, true

}

return nil, false
}
35 changes: 35 additions & 0 deletions v2/pkg/ast/ast_selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ func (d *Document) AddSelection(set int, selection Selection) {
d.SelectionSets[set].SelectionRefs = append(d.SelectionSets[set].SelectionRefs, d.AddSelectionToDocument(selection))
}

func (d *Document) AddSelectionRefToSelectionSet(set int, selectionRef int) {
d.SelectionSets[set].SelectionRefs = append(d.SelectionSets[set].SelectionRefs, selectionRef)
}

func (d *Document) EmptySelectionSet(ref int) {
d.SelectionSets[ref].SelectionRefs = d.SelectionSets[ref].SelectionRefs[:0]
}
Expand Down Expand Up @@ -202,3 +206,34 @@ func (d *Document) SelectionSetHasFieldSelectionWithExactName(set int, name []by
}
return false, InvalidRef
}

func (d *Document) SelectionSetFieldSelections(set int) (refs []int) {
for _, selectionRef := range d.SelectionSets[set].SelectionRefs {
if d.Selections[selectionRef].Kind == SelectionKindField {
refs = append(refs, selectionRef)
}
}
return
}

func (d *Document) SelectionSetInlineFragmentSelections(set int) (refs []int) {
for _, selectionRef := range d.SelectionSets[set].SelectionRefs {
if d.Selections[selectionRef].Kind == SelectionKindInlineFragment {
refs = append(refs, selectionRef)
}
}
return
}

func (d *Document) SelectionKind(ref int) SelectionKind {
return d.Selections[ref].Kind
}

func (d *Document) SelectionSetFieldNames(set int) (fieldNames []string) {
fieldSelections := d.SelectionSetFieldSelections(set)
fieldNames = make([]string, 0, len(fieldSelections))
for _, fieldSelectionRef := range fieldSelections {
fieldNames = append(fieldNames, d.FieldNameString(d.Selections[fieldSelectionRef].Ref))
}
return
}
12 changes: 12 additions & 0 deletions v2/pkg/ast/ast_union_type_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,15 @@ func (d *Document) ImportUnionTypeDefinitionWithDirectives(name, description str

return
}

func (d *Document) UnionTypeDefinitionMemberTypeNames(ref int) (typeNames []string, ok bool) {
if !d.UnionTypeDefinitions[ref].HasUnionMemberTypes {
return nil, false
}

typeNames = make([]string, 0, len(d.UnionTypeDefinitions[ref].UnionMemberTypes.Refs))
for _, typeRef := range d.UnionTypeDefinitions[ref].UnionMemberTypes.Refs {
typeNames = append(typeNames, d.TypeNameString(typeRef))
}
return typeNames, true
}
74 changes: 58 additions & 16 deletions v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,58 @@ type Planner struct {
addedInlineFragments map[onTypeInlineFragment]struct{}
hasFederationRoot bool
extractEntities bool

// tmp
upstreamSchema *ast.Document
}

type onTypeInlineFragment struct {
TypeCondition string
SelectionSet int
}

func (p *Planner) UpstreamSchema(dataSourceConfig plan.DataSourceConfiguration) *ast.Document {
if p.upstreamSchema != nil {
return p.upstreamSchema
}

var config Configuration

err := json.Unmarshal(dataSourceConfig.Custom, &config)
if err != nil {
panic(err)
}

definition := ast.NewDocument()
definitionParser := astparser.NewParser()
report := &operationreport.Report{}

if config.Federation.Enabled {
federationSchema, err := federation.BuildFederationSchema(config.UpstreamSchema, config.Federation.ServiceSDL)
if err != nil {
panic(err)
}
definition.Input.ResetInputString(federationSchema)
definitionParser.Parse(definition, report)
if report.HasErrors() {
panic(report)
}
} else {
definition.Input.ResetInputString(config.UpstreamSchema)
definitionParser.Parse(definition, report)
if report.HasErrors() {
panic(report)
}

if err := asttransform.MergeDefinitionWithBaseSchema(definition); err != nil {
panic(fmt.Errorf("unable to merge upstream schema with base schema: %v", err))
}
}

p.upstreamSchema = definition
return definition
}

func (p *Planner) SetID(id int) {
p.id = id
}
Expand Down Expand Up @@ -318,7 +363,7 @@ func (p *Planner) Register(visitor *plan.Visitor, configuration plan.DataSourceC
return nil
}

func (p *Planner) ConfigureFetch() plan.FetchConfiguration {
func (p *Planner) ConfigureFetch() resolve.FetchConfiguration {
var input []byte
input = httpclient.SetInputBodyWithPath(input, p.upstreamVariables, "variables")
input = httpclient.SetInputBodyWithPath(input, p.printOperation(), "query")
Expand All @@ -344,15 +389,16 @@ func (p *Planner) ConfigureFetch() plan.FetchConfiguration {
}
}

return plan.FetchConfiguration{
return resolve.FetchConfiguration{
Input: string(input),
DataSource: &Source{
httpClient: p.fetchClient,
},
Variables: p.variables,
DisallowSingleFlight: p.disallowSingleFlight,
RequiresSerialFetch: p.requiresSerialFetch(),
RequiresBatchFetch: p.requiresBatchFetch(),
RequiresEntityFetch: p.requiresEntityFetch(),
RequiresEntityBatchFetch: p.requiresEntityBatchFetch(),
PostProcessing: postProcessing,
SetTemplateOutputToNullOnVariableNull: p.extractEntities,
}
Expand All @@ -378,12 +424,12 @@ func (p *Planner) requiresSerialFetch() bool {
return false
}

func (p *Planner) requiresBatchFetch() bool {
if !p.dataSourcePlannerConfig.HasRequiredFields() {
return false
}
func (p *Planner) requiresEntityFetch() bool {
return p.dataSourcePlannerConfig.HasRequiredFields() && p.dataSourcePlannerConfig.PathType == plan.PlannerPathObject
}

return p.dataSourcePlannerConfig.PathType != plan.PlannerPathObject
func (p *Planner) requiresEntityBatchFetch() bool {
return p.dataSourcePlannerConfig.HasRequiredFields() && p.dataSourcePlannerConfig.PathType != plan.PlannerPathObject
}

func (p *Planner) ConfigureSubscription() plan.SubscriptionConfiguration {
Expand Down Expand Up @@ -750,16 +796,9 @@ func (p *Planner) addRepresentationsVariable() {
}

func (p *Planner) buildRepresentationsVariable() resolve.Variable {
isArrayItems := p.dataSourcePlannerConfig.PathType != plan.PlannerPathObject

uniqTypes := p.dataSourcePlannerConfig.RequiredFields.UniqueTypes()
if len(uniqTypes) > 1 && !isArrayItems {
p.stopWithError("unhandled case: more than one type requirements for non-array path type")
}

objects := make([]*resolve.Object, 0, len(p.dataSourcePlannerConfig.RequiredFields))
for _, cfg := range p.dataSourcePlannerConfig.RequiredFields {
node, err := buildRepresentationVariableNode(cfg, p.visitor.Definition, false, isArrayItems)
node, err := buildRepresentationVariableNode(cfg, p.visitor.Definition)
if err != nil {
p.visitor.Walker.StopWithInternalErr(err)
return nil
Expand Down Expand Up @@ -1269,6 +1308,9 @@ func (p *Planner) printQueryPlan(operation *ast.Document) {

args := []interface{}{
"Execution plan:\n",
"Planner path: ",
p.dataSourcePlannerConfig.ParentPath,
"\n",
}

if len(p.upstreamVariables) > 0 {
Expand Down
Loading
Loading