Repository: jensneuse/graphql-go-tools (go)

Issue:
Resolver don't return an error in case of nested non-nullable object error
Here is a [link](https://github.com/chedom/graphql-go-tools/blob/temp/example-resolve-non-nullable-nested-object/examples/federation/start.sh) with prepared example (**product** server always returns an error for  resolving  **top-3** product).

query:
```
query {
  topProducts {
    reviews {
      product {
        price
      }
    }
  }
}
```

got:
```
{
  "data": {
    "topProducts": [
      {
        "reviews": [
          {
            "product": {
              "price": 11
            }
          },
          ...
        ]
      },
      {
        "reviews": null
      },
      {
        "reviews": [
          null
        ]
      }
    ]
  }
}
```

expected:
```
{
  "errors": [
    {
      "message": "some custom error",
      "path": [
        "_entities"
      ]
    }
  ],
  "data": {
    "topProducts": [
      {
        "reviews": [
          {
            "product": {
              "price": 11
            }
          },
       ...
        ]
      },
      {
        "reviews": null
      },
      {
        "reviews": [
          null
        ]
      }
    ]
  }
}
```

Pull request:
fix resolving error for nested non-nullable object
Fixes #262 

Patch:
diff --git a/pkg/engine/resolve/resolve.go b/pkg/engine/resolve/resolve.go
index c98c2ce3..2b16a311 100644
--- a/pkg/engine/resolve/resolve.go
+++ b/pkg/engine/resolve/resolve.go
@@ -890,6 +890,7 @@ func (r *Resolver) resolveObject(ctx *Context, object *Object, data []byte, obje
 			}
 			if errors.Is(err, errNonNullableFieldValueIsNull) && object.Nullable {
 				objectBuf.Data.Reset()
+				r.MergeBufPairErrors(fieldBuf, objectBuf)
 				r.resolveNull(objectBuf.Data)
 				return nil
 			}


Test patch:
diff --git a/pkg/engine/resolve/resolve_test.go b/pkg/engine/resolve/resolve_test.go
index ae9cc5a3..a6fe4499 100644
--- a/pkg/engine/resolve/resolve_test.go
+++ b/pkg/engine/resolve/resolve_test.go
@@ -788,6 +788,52 @@ func TestResolver_ResolveGraphQLResponse(t *testing.T) {
 			},
 		}, Context{Context: context.Background()}, `{"errors":[{"message":"errorMessage"}],"data":{"name":null}}`
 	}))
+	t.Run("nested fetch error for non-nullable field", testFn(func(t *testing.T, r *Resolver, ctrl *gomock.Controller) (node *GraphQLResponse, ctx Context, expectedOutput string) {
+		r.EnableSingleFlightLoader = true
+		mockDataSource := NewMockDataSource(ctrl)
+		mockDataSource.EXPECT().UniqueIdentifier().Return([]byte("mock"))
+		mockDataSource.EXPECT().
+			Load(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&BufPair{})).
+			Do(func(ctx context.Context, input []byte, pair *BufPair) (err error) {
+				pair.WriteErr([]byte("errorMessage"), nil, nil)
+				return
+			}).
+			Return(nil)
+		return &GraphQLResponse{
+			Data: &Object{
+				Nullable: true,
+				Fetch: &SingleFetch{
+					BufferId:   0,
+					DataSource: FakeDataSource(`{"id":1}`),
+				},
+				Fields: []*Field{
+					{
+						HasBuffer: true,
+						BufferID:  0,
+						Name:      []byte("nestedObject"),
+						Value: &Object{
+							Nullable: false,
+							Fetch: &SingleFetch{
+								BufferId:   1,
+								DataSource: mockDataSource,
+							},
+							Fields: []*Field{
+								{
+									HasBuffer: true,
+									BufferID: 1,
+									Name: []byte("foo"),
+									Value: &String{
+										Path: []string{"foo"},
+										Nullable: false,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		}, Context{Context: context.Background()}, `{"errors":[{"message":"errorMessage"}],"data":null}`
+	}))
 	t.Run("fetch with two Errors", testFn(func(t *testing.T, r *Resolver, ctrl *gomock.Controller) (node *GraphQLResponse, ctx Context, expectedOutput string) {
 		r.EnableSingleFlightLoader = true
 		mockDataSource := NewMockDataSource(ctrl)