Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ By [@USERNAME](https://github.com/USERNAME) in https://github.com/apollographql/
# [x.x.x] (unreleased) - 2022-mm-dd
## ❗ BREAKING ❗

### Router debug Docker images now run under the control of heaptrack ([Issue #2135](https://github.com/apollographql/router/pull/2142))
### Router debug Docker images now run under the control of heaptrack ([Issue #2135](https://github.com/apollographql/router/issues/2135))

From the next release, our debug Docker image will invoke the router under the control of heaptrack. We are making this change to make it simple for users to investigate potential memory issues with the router.

Expand Down Expand Up @@ -115,7 +115,7 @@ telemetry:
headers: true
```

### Provide multi-arch (amd64/arm64) Docker images for the Router ([Issue #1932](https://github.com/apollographql/router/pull/2138))
### Provide multi-arch (amd64/arm64) Docker images for the Router ([Issue #1932](https://github.com/apollographql/router/issues/1932))

From the next release, our Docker images will be multi-arch.

Expand Down Expand Up @@ -261,6 +261,12 @@ By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/p

## 🛠 Maintenance

### Verify that deferred fragment acts as a boundary for nullability rules ([Issue #2169](https://github.com/apollographql/router/issues/2169))

Add a test to ensure that deferred fragments act as a boundary for nullability rules.

By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2183

### Refactor APQ ([PR #2129](https://github.com/apollographql/router/pull/2129))

Remove duplicated code.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
source: apollo-router/src/services/supergraph_service.rs
expression: stream.next_response().await.unwrap()
---
{
"hasNext": false,
"incremental": [
{
"data": null,
"path": [
"currentUser",
"activeOrganization",
"suborga",
0
],
"extensions": {
"valueCompletion": [
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
0
]
},
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
1
]
},
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
2
]
}
]
}
},
{
"data": null,
"path": [
"currentUser",
"activeOrganization",
"suborga",
1
],
"extensions": {
"valueCompletion": [
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
0
]
},
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
1
]
},
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
2
]
}
]
}
},
{
"data": null,
"path": [
"currentUser",
"activeOrganization",
"suborga",
2
],
"extensions": {
"valueCompletion": [
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
0
]
},
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
1
]
},
{
"message": "Cannot return null for non-nullable field Organization.nonNullId",
"path": [
"currentUser",
"activeOrganization",
"suborga",
2
]
}
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: apollo-router/src/services/supergraph_service.rs
expression: stream.next_response().await.unwrap()
---
{
"data": {
"currentUser": {
"activeOrganization": {
"id": "0",
"suborga": [
{
"id": "1"
},
{
"id": "2"
},
{
"id": "3"
}
]
}
}
},
"hasNext": true
}
82 changes: 82 additions & 0 deletions apollo-router/src/services/supergraph_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,88 @@ mod tests {
insta::assert_json_snapshot!(stream.next_response().await.unwrap());
}

#[tokio::test]
async fn deferred_fragment_bounds_nullability() {
let subgraphs = MockedSubgraphs([
("user", MockSubgraph::builder().with_json(
serde_json::json!{{"query":"{currentUser{activeOrganization{__typename id}}}"}},
serde_json::json!{{"data": {"currentUser": { "activeOrganization": { "__typename": "Organization", "id": "0" } }}}}
).build()),
("orga", MockSubgraph::builder().with_json(
serde_json::json!{{
"query":"query($representations:[_Any!]!){_entities(representations:$representations){...on Organization{suborga{__typename id}}}}",
"variables": {
"representations":[{"__typename": "Organization", "id":"0"}]
}
}},
serde_json::json!{{
"data": {
"_entities": [{ "suborga": [
{ "__typename": "Organization", "id": "1"},
{ "__typename": "Organization", "id": "2"},
{ "__typename": "Organization", "id": "3"},
] }]
},
}}
)
.with_json(
serde_json::json!{{
"query":"query($representations:[_Any!]!){_entities(representations:$representations){...on Organization{name}}}",
"variables": {
"representations":[
{"__typename": "Organization", "id":"1"},
{"__typename": "Organization", "id":"2"},
{"__typename": "Organization", "id":"3"}

]
}
}},
serde_json::json!{{
"data": {
"_entities": [
{ "__typename": "Organization", "id": "1"},
{ "__typename": "Organization", "id": "2", "name": "A"},
{ "__typename": "Organization", "id": "3"},
]
},
"errors": [
{
"message": "error orga 1",
"path": ["_entities", 0],
},
{
"message": "error orga 3",
"path": ["_entities", 2],
}
]
}}
).build())
].into_iter().collect());

let service = TestHarness::builder()
.configuration_json(serde_json::json!({"include_subgraph_errors": { "all": true } }))
.unwrap()
.schema(SCHEMA)
.extra_plugin(subgraphs)
.build()
.await
.unwrap();

let request = supergraph::Request::fake_builder()
.header("Accept", "multipart/mixed; deferSpec=20220824")
.query(
"query { currentUser { activeOrganization { id suborga { id ...@defer { nonNullId } } } } }",
)
.build()
.unwrap();

let mut stream = service.oneshot(request).await.unwrap();

insta::assert_json_snapshot!(stream.next_response().await.unwrap());

insta::assert_json_snapshot!(stream.next_response().await.unwrap());
}

#[tokio::test]
async fn errors_on_incremental_responses() {
let subgraphs = MockedSubgraphs([
Expand Down