@apollo/gateway 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/config.d.ts +42 -16
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +28 -18
  4. package/dist/config.js.map +1 -1
  5. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  6. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  7. package/dist/datasources/RemoteGraphQLDataSource.js +4 -1
  8. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  9. package/dist/executeQueryPlan.d.ts.map +1 -1
  10. package/dist/executeQueryPlan.js +2 -2
  11. package/dist/executeQueryPlan.js.map +1 -1
  12. package/dist/index.d.ts +35 -23
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +205 -308
  15. package/dist/index.js.map +1 -1
  16. package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
  17. package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
  18. package/dist/schema-helper/addResolversToSchema.js +62 -0
  19. package/dist/schema-helper/addResolversToSchema.js.map +1 -0
  20. package/dist/schema-helper/error.d.ts +6 -0
  21. package/dist/schema-helper/error.d.ts.map +1 -0
  22. package/dist/schema-helper/error.js +14 -0
  23. package/dist/schema-helper/error.js.map +1 -0
  24. package/dist/schema-helper/index.d.ts +4 -0
  25. package/dist/schema-helper/index.d.ts.map +1 -0
  26. package/dist/schema-helper/index.js +16 -0
  27. package/dist/schema-helper/index.js.map +1 -0
  28. package/dist/schema-helper/resolverMap.d.ts +16 -0
  29. package/dist/schema-helper/resolverMap.d.ts.map +1 -0
  30. package/dist/schema-helper/resolverMap.js +3 -0
  31. package/dist/schema-helper/resolverMap.js.map +1 -0
  32. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
  33. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
  34. package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
  35. package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
  36. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
  37. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
  38. package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
  39. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
  40. package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
  41. package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
  42. package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
  43. package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
  44. package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
  45. package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
  46. package/dist/supergraphManagers/LocalCompose/index.js +55 -0
  47. package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
  48. package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
  49. package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
  50. package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
  51. package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
  52. package/dist/{loadSupergraphSdlFromStorage.d.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts} +1 -1
  53. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  54. package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +1 -1
  55. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
  56. package/dist/{outOfBandReporter.d.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts} +0 -0
  57. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
  58. package/dist/{outOfBandReporter.js → supergraphManagers/UplinkFetcher/outOfBandReporter.js} +2 -2
  59. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
  60. package/dist/supergraphManagers/index.d.ts +5 -0
  61. package/dist/supergraphManagers/index.d.ts.map +1 -0
  62. package/dist/supergraphManagers/index.js +12 -0
  63. package/dist/supergraphManagers/index.js.map +1 -0
  64. package/dist/utilities/createHash.d.ts +2 -0
  65. package/dist/utilities/createHash.d.ts.map +1 -0
  66. package/dist/utilities/createHash.js +15 -0
  67. package/dist/utilities/createHash.js.map +1 -0
  68. package/dist/utilities/isNodeLike.d.ts +3 -0
  69. package/dist/utilities/isNodeLike.d.ts.map +1 -0
  70. package/dist/utilities/isNodeLike.js +8 -0
  71. package/dist/utilities/isNodeLike.js.map +1 -0
  72. package/package.json +12 -10
  73. package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
  74. package/src/__tests__/executeQueryPlan.test.ts +637 -1
  75. package/src/__tests__/execution-utils.ts +3 -3
  76. package/src/__tests__/gateway/buildService.test.ts +3 -3
  77. package/src/__tests__/gateway/endToEnd.test.ts +1 -1
  78. package/src/__tests__/gateway/executor.test.ts +1 -1
  79. package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -99
  80. package/src/__tests__/gateway/opentelemetry.test.ts +8 -3
  81. package/src/__tests__/gateway/queryPlanCache.test.ts +25 -9
  82. package/src/__tests__/gateway/reporting.test.ts +44 -19
  83. package/src/__tests__/gateway/supergraphSdl.test.ts +394 -0
  84. package/src/__tests__/integration/aliases.test.ts +9 -3
  85. package/src/__tests__/integration/configuration.test.ts +109 -12
  86. package/src/__tests__/integration/logger.test.ts +1 -1
  87. package/src/__tests__/integration/networkRequests.test.ts +81 -118
  88. package/src/__tests__/integration/nockMocks.ts +15 -8
  89. package/src/config.ts +149 -40
  90. package/src/datasources/LocalGraphQLDataSource.ts +1 -1
  91. package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
  92. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
  93. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
  94. package/src/executeQueryPlan.ts +14 -2
  95. package/src/index.ts +314 -485
  96. package/src/schema-helper/addResolversToSchema.ts +83 -0
  97. package/src/schema-helper/error.ts +11 -0
  98. package/src/schema-helper/index.ts +3 -0
  99. package/src/schema-helper/resolverMap.ts +23 -0
  100. package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
  101. package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +5 -5
  102. package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
  103. package/src/supergraphManagers/IntrospectAndCompose/index.ts +163 -0
  104. package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +6 -6
  105. package/src/supergraphManagers/LegacyFetcher/index.ts +229 -0
  106. package/src/supergraphManagers/LocalCompose/index.ts +83 -0
  107. package/src/{__tests__ → supergraphManagers/UplinkFetcher/__tests__}/loadSupergraphSdlFromStorage.test.ts +4 -4
  108. package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
  109. package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
  110. package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +3 -3
  111. package/src/{outOfBandReporter.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.ts} +2 -2
  112. package/src/supergraphManagers/index.ts +4 -0
  113. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +3 -3
  114. package/src/utilities/createHash.ts +10 -0
  115. package/src/utilities/isNodeLike.ts +11 -0
  116. package/CHANGELOG.md +0 -470
  117. package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
  118. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
  119. package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
  120. package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
  121. package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
  122. package/dist/outOfBandReporter.d.ts.map +0 -1
  123. package/dist/outOfBandReporter.js.map +0 -1
  124. package/src/__tests__/gateway/composedSdl.test.ts +0 -44
@@ -5,7 +5,6 @@ import {
5
5
  GraphQLSchema,
6
6
  print,
7
7
  } from 'graphql';
8
- import { addResolversToSchema, GraphQLResolverMap } from 'apollo-graphql';
9
8
  import gql from 'graphql-tag';
10
9
  import { GraphQLRequestContext, VariableValues } from 'apollo-server-types';
11
10
  import { AuthenticationError } from 'apollo-server-core';
@@ -21,6 +20,7 @@ import { buildComposedSchema, QueryPlanner } from '@apollo/query-planner';
21
20
  import { ApolloGateway } from '..';
22
21
  import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
23
22
  import { getFederatedTestingSchema } from './execution-utils';
23
+ import { addResolversToSchema, GraphQLResolverMap } from '../schema-helper';
24
24
 
25
25
  expect.addSnapshotSerializer(astSerializer);
26
26
  expect.addSnapshotSerializer(queryPlanSerializer);
@@ -1636,4 +1636,640 @@ describe('executeQueryPlan', () => {
1636
1636
  });
1637
1637
  });
1638
1638
  });
1639
+
1640
+ describe('@requires', () => {
1641
+ test('handles null in required field correctly (with nullable fields)', async () => {
1642
+ const s1 = {
1643
+ name: 'S1',
1644
+ typeDefs: gql`
1645
+ type T1 @key(fields: "id") {
1646
+ id: Int!
1647
+ f1: String
1648
+ }
1649
+ `
1650
+ }
1651
+
1652
+ const s2 = {
1653
+ name: 'S2',
1654
+ typeDefs: gql`
1655
+ type Query {
1656
+ getT1s: [T1]
1657
+ }
1658
+
1659
+ extend type T1 @key(fields: "id") {
1660
+ id: Int! @external
1661
+ f1: String @external
1662
+ f2: T2 @requires(fields: "f1")
1663
+ }
1664
+
1665
+ type T2 {
1666
+ a: String
1667
+ }
1668
+ `
1669
+ }
1670
+
1671
+ const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
1672
+
1673
+ const s1_data = [
1674
+ { id: 0, f1: "foo" },
1675
+ { id: 1, f1: null },
1676
+ { id: 2, f1: "bar" },
1677
+ ];
1678
+
1679
+ addResolversToSchema(serviceMap['S1'].schema, {
1680
+ T1: {
1681
+ __resolveReference(ref) {
1682
+ return s1_data[ref.id];
1683
+ },
1684
+ },
1685
+ });
1686
+
1687
+ addResolversToSchema(serviceMap['S2'].schema, {
1688
+ Query: {
1689
+ getT1s() {
1690
+ return [{id: 0}, {id: 1}, {id: 2}];
1691
+ },
1692
+ },
1693
+ T1: {
1694
+ __resolveReference(ref) {
1695
+ // the ref has already the id and f1 is a require is triggered, and we resolve f2 below
1696
+ return ref;
1697
+ },
1698
+ f2(o) {
1699
+ return o.f1 === null ? null : { a: `t1:${o.f1}` };
1700
+ }
1701
+ }
1702
+ });
1703
+
1704
+ const operationDocument = gql`
1705
+ query {
1706
+ getT1s {
1707
+ id
1708
+ f1
1709
+ f2 {
1710
+ a
1711
+ }
1712
+ }
1713
+ }
1714
+ `;
1715
+
1716
+ const operationContext = buildOperationContext({
1717
+ schema,
1718
+ operationDocument,
1719
+ });
1720
+
1721
+ const queryPlan = queryPlanner.buildQueryPlan(operationContext);
1722
+ expect(queryPlan).toMatchInlineSnapshot(`
1723
+ QueryPlan {
1724
+ Sequence {
1725
+ Fetch(service: "S2") {
1726
+ {
1727
+ getT1s {
1728
+ id
1729
+ __typename
1730
+ }
1731
+ }
1732
+ },
1733
+ Flatten(path: "getT1s.@") {
1734
+ Fetch(service: "S1") {
1735
+ {
1736
+ ... on T1 {
1737
+ __typename
1738
+ id
1739
+ }
1740
+ } =>
1741
+ {
1742
+ ... on T1 {
1743
+ f1
1744
+ __typename
1745
+ id
1746
+ }
1747
+ }
1748
+ },
1749
+ },
1750
+ Flatten(path: "getT1s.@") {
1751
+ Fetch(service: "S2") {
1752
+ {
1753
+ ... on T1 {
1754
+ __typename
1755
+ id
1756
+ f1
1757
+ }
1758
+ } =>
1759
+ {
1760
+ ... on T1 {
1761
+ f2 {
1762
+ a
1763
+ }
1764
+ }
1765
+ }
1766
+ },
1767
+ },
1768
+ },
1769
+ }
1770
+ `);
1771
+ const response = await executeQueryPlan(queryPlan, serviceMap, buildRequestContext(), operationContext);
1772
+ expect(response.data).toMatchInlineSnapshot(`
1773
+ Object {
1774
+ "getT1s": Array [
1775
+ Object {
1776
+ "f1": "foo",
1777
+ "f2": Object {
1778
+ "a": "t1:foo",
1779
+ },
1780
+ "id": 0,
1781
+ },
1782
+ Object {
1783
+ "f1": null,
1784
+ "f2": null,
1785
+ "id": 1,
1786
+ },
1787
+ Object {
1788
+ "f1": "bar",
1789
+ "f2": Object {
1790
+ "a": "t1:bar",
1791
+ },
1792
+ "id": 2,
1793
+ },
1794
+ ],
1795
+ }
1796
+ `);
1797
+ expect(response.errors).toBeUndefined();
1798
+ });
1799
+
1800
+ test('handles null in required field correctly (with @require field non-nullable)', async () => {
1801
+ const s1 = {
1802
+ name: 'S1',
1803
+ typeDefs: gql`
1804
+ type T1 @key(fields: "id") {
1805
+ id: Int!
1806
+ f1: String
1807
+ }
1808
+ `
1809
+ }
1810
+
1811
+ const s2 = {
1812
+ name: 'S2',
1813
+ typeDefs: gql`
1814
+ type Query {
1815
+ getT1s: [T1]
1816
+ }
1817
+
1818
+ extend type T1 @key(fields: "id") {
1819
+ id: Int! @external
1820
+ f1: String @external
1821
+ f2: T2! @requires(fields: "f1")
1822
+ }
1823
+
1824
+ type T2 {
1825
+ a: String
1826
+ }
1827
+ `
1828
+ }
1829
+
1830
+ const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
1831
+
1832
+ const s1_data = [
1833
+ { id: 0, f1: "foo" },
1834
+ { id: 1, f1: null },
1835
+ { id: 2, f1: "bar" },
1836
+ ];
1837
+
1838
+ addResolversToSchema(serviceMap['S1'].schema, {
1839
+ T1: {
1840
+ __resolveReference(ref) {
1841
+ return s1_data[ref.id];
1842
+ },
1843
+ },
1844
+ });
1845
+
1846
+ addResolversToSchema(serviceMap['S2'].schema, {
1847
+ Query: {
1848
+ getT1s() {
1849
+ return [{id: 0}, {id: 1}, {id: 2}];
1850
+ },
1851
+ },
1852
+ T1: {
1853
+ __resolveReference(ref) {
1854
+ // the ref has already the id and f1 is a require is triggered, and we resolve f2 below
1855
+ return ref;
1856
+ },
1857
+ f2(o) {
1858
+ return o.f1 === null ? null : { a: `t1:${o.f1}` };
1859
+ }
1860
+ }
1861
+ });
1862
+
1863
+ const operationDocument = gql`
1864
+ query {
1865
+ getT1s {
1866
+ id
1867
+ f1
1868
+ f2 {
1869
+ a
1870
+ }
1871
+ }
1872
+ }
1873
+ `;
1874
+
1875
+ const operationContext = buildOperationContext({
1876
+ schema,
1877
+ operationDocument,
1878
+ });
1879
+
1880
+ const queryPlan = queryPlanner.buildQueryPlan(operationContext);
1881
+ expect(queryPlan).toMatchInlineSnapshot(`
1882
+ QueryPlan {
1883
+ Sequence {
1884
+ Fetch(service: "S2") {
1885
+ {
1886
+ getT1s {
1887
+ id
1888
+ __typename
1889
+ }
1890
+ }
1891
+ },
1892
+ Flatten(path: "getT1s.@") {
1893
+ Fetch(service: "S1") {
1894
+ {
1895
+ ... on T1 {
1896
+ __typename
1897
+ id
1898
+ }
1899
+ } =>
1900
+ {
1901
+ ... on T1 {
1902
+ f1
1903
+ __typename
1904
+ id
1905
+ }
1906
+ }
1907
+ },
1908
+ },
1909
+ Flatten(path: "getT1s.@") {
1910
+ Fetch(service: "S2") {
1911
+ {
1912
+ ... on T1 {
1913
+ __typename
1914
+ id
1915
+ f1
1916
+ }
1917
+ } =>
1918
+ {
1919
+ ... on T1 {
1920
+ f2 {
1921
+ a
1922
+ }
1923
+ }
1924
+ }
1925
+ },
1926
+ },
1927
+ },
1928
+ }
1929
+ `);
1930
+
1931
+ const response = await executeQueryPlan(queryPlan, serviceMap, buildRequestContext(), operationContext);
1932
+ // `null` should bubble up since `f2` is now non-nullable. But we should still get the `id: 0` response.
1933
+ expect(response.data).toMatchInlineSnapshot(`
1934
+ Object {
1935
+ "getT1s": Array [
1936
+ Object {
1937
+ "f1": "foo",
1938
+ "f2": Object {
1939
+ "a": "t1:foo",
1940
+ },
1941
+ "id": 0,
1942
+ },
1943
+ null,
1944
+ Object {
1945
+ "f1": "bar",
1946
+ "f2": Object {
1947
+ "a": "t1:bar",
1948
+ },
1949
+ "id": 2,
1950
+ },
1951
+ ],
1952
+ }
1953
+ `);
1954
+
1955
+ // We returning `null` for f2 which isn't nullable, so it bubbled up and we should have an error
1956
+ expect(response.errors?.map((e) => e.message)).toStrictEqual(['Cannot return null for non-nullable field T1.f2.']);
1957
+ });
1958
+
1959
+ test('handles null in required field correctly (with non-nullable required field)', async () => {
1960
+ const s1 = {
1961
+ name: 'S1',
1962
+ typeDefs: gql`
1963
+ type T1 @key(fields: "id") {
1964
+ id: Int!
1965
+ f1: String!
1966
+ }
1967
+ `
1968
+ }
1969
+
1970
+ const s2 = {
1971
+ name: 'S2',
1972
+ typeDefs: gql`
1973
+ type Query {
1974
+ getT1s: [T1]
1975
+ }
1976
+
1977
+ extend type T1 @key(fields: "id") {
1978
+ id: Int! @external
1979
+ f1: String! @external
1980
+ f2: T2 @requires(fields: "f1")
1981
+ }
1982
+
1983
+ type T2 {
1984
+ a: String
1985
+ }
1986
+ `
1987
+ }
1988
+
1989
+ const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
1990
+
1991
+ const s1_data = [
1992
+ { id: 0, f1: "foo" },
1993
+ { id: 1, f1: null },
1994
+ { id: 2, f1: "bar" },
1995
+ ];
1996
+
1997
+ addResolversToSchema(serviceMap['S1'].schema, {
1998
+ T1: {
1999
+ __resolveReference(ref) {
2000
+ return s1_data[ref.id];
2001
+ },
2002
+ },
2003
+ });
2004
+
2005
+ addResolversToSchema(serviceMap['S2'].schema, {
2006
+ Query: {
2007
+ getT1s() {
2008
+ return [{id: 0}, {id: 1}, {id: 2}];
2009
+ },
2010
+ },
2011
+ T1: {
2012
+ __resolveReference(ref) {
2013
+ // the ref has already the id and f1 is a require is triggered, and we resolve f2 below
2014
+ return ref;
2015
+ },
2016
+ f2(o) {
2017
+ return o.f1 === null ? null : { a: `t1:${o.f1}` };
2018
+ }
2019
+ }
2020
+ });
2021
+
2022
+ const operationDocument = gql`
2023
+ query {
2024
+ getT1s {
2025
+ id
2026
+ f1
2027
+ f2 {
2028
+ a
2029
+ }
2030
+ }
2031
+ }
2032
+ `;
2033
+ const operationContext = buildOperationContext({
2034
+ schema,
2035
+ operationDocument,
2036
+ });
2037
+
2038
+ const queryPlan = queryPlanner.buildQueryPlan(operationContext);
2039
+ expect(queryPlan).toMatchInlineSnapshot(`
2040
+ QueryPlan {
2041
+ Sequence {
2042
+ Fetch(service: "S2") {
2043
+ {
2044
+ getT1s {
2045
+ id
2046
+ __typename
2047
+ }
2048
+ }
2049
+ },
2050
+ Flatten(path: "getT1s.@") {
2051
+ Fetch(service: "S1") {
2052
+ {
2053
+ ... on T1 {
2054
+ __typename
2055
+ id
2056
+ }
2057
+ } =>
2058
+ {
2059
+ ... on T1 {
2060
+ f1
2061
+ __typename
2062
+ id
2063
+ }
2064
+ }
2065
+ },
2066
+ },
2067
+ Flatten(path: "getT1s.@") {
2068
+ Fetch(service: "S2") {
2069
+ {
2070
+ ... on T1 {
2071
+ __typename
2072
+ id
2073
+ f1
2074
+ }
2075
+ } =>
2076
+ {
2077
+ ... on T1 {
2078
+ f2 {
2079
+ a
2080
+ }
2081
+ }
2082
+ }
2083
+ },
2084
+ },
2085
+ },
2086
+ }
2087
+ `);
2088
+
2089
+ const response = await executeQueryPlan(queryPlan, serviceMap, buildRequestContext(), operationContext);
2090
+ // `null` should bubble up since `f2` is now non-nullable. But we should still get the `id: 0` response.
2091
+ expect(response.data).toMatchInlineSnapshot(`
2092
+ Object {
2093
+ "getT1s": Array [
2094
+ Object {
2095
+ "f1": "foo",
2096
+ "f2": Object {
2097
+ "a": "t1:foo",
2098
+ },
2099
+ "id": 0,
2100
+ },
2101
+ null,
2102
+ Object {
2103
+ "f1": "bar",
2104
+ "f2": Object {
2105
+ "a": "t1:bar",
2106
+ },
2107
+ "id": 2,
2108
+ },
2109
+ ],
2110
+ }
2111
+ `);
2112
+ expect(response.errors?.map((e) => e.message)).toStrictEqual(['Cannot return null for non-nullable field T1.f1.']);
2113
+ });
2114
+
2115
+ test('handles errors in required field correctly (with nullable fields)', async () => {
2116
+ const s1 = {
2117
+ name: 'S1',
2118
+ typeDefs: gql`
2119
+ type T1 @key(fields: "id") {
2120
+ id: Int!
2121
+ f1: String
2122
+ }
2123
+ `
2124
+ }
2125
+
2126
+ const s2 = {
2127
+ name: 'S2',
2128
+ typeDefs: gql`
2129
+ type Query {
2130
+ getT1s: [T1]
2131
+ }
2132
+
2133
+ extend type T1 @key(fields: "id") {
2134
+ id: Int! @external
2135
+ f1: String @external
2136
+ f2: T2 @requires(fields: "f1")
2137
+ }
2138
+
2139
+ type T2 {
2140
+ a: String
2141
+ }
2142
+ `
2143
+ }
2144
+
2145
+ const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
2146
+
2147
+ addResolversToSchema(serviceMap['S1'].schema, {
2148
+ T1: {
2149
+ __resolveReference(ref) {
2150
+ return ref;
2151
+ },
2152
+ f1(o) {
2153
+ switch (o.id) {
2154
+ case 0: return "foo";
2155
+ case 1: return [ "invalid" ]; // This will effectively throw
2156
+ case 2: return "bar";
2157
+ default: throw new Error('Not handled');
2158
+ }
2159
+ }
2160
+ },
2161
+ });
2162
+
2163
+ addResolversToSchema(serviceMap['S2'].schema, {
2164
+ Query: {
2165
+ getT1s() {
2166
+ return [{id: 0}, {id: 1}, {id: 2}];
2167
+ },
2168
+ },
2169
+ T1: {
2170
+ __resolveReference(ref) {
2171
+ // the ref has already the id and f1 is a require is triggered, and we resolve f2 below
2172
+ return ref;
2173
+ },
2174
+ f2(o) {
2175
+ return o.f1 === null ? null : { a: `t1:${o.f1}` };
2176
+ }
2177
+ }
2178
+ });
2179
+
2180
+ const operationDocument = gql`
2181
+ query {
2182
+ getT1s {
2183
+ id
2184
+ f1
2185
+ f2 {
2186
+ a
2187
+ }
2188
+ }
2189
+ }
2190
+ `;
2191
+ const operationContext = buildOperationContext({
2192
+ schema,
2193
+ operationDocument,
2194
+ });
2195
+
2196
+ const queryPlan = queryPlanner.buildQueryPlan(operationContext);
2197
+ expect(queryPlan).toMatchInlineSnapshot(`
2198
+ QueryPlan {
2199
+ Sequence {
2200
+ Fetch(service: "S2") {
2201
+ {
2202
+ getT1s {
2203
+ id
2204
+ __typename
2205
+ }
2206
+ }
2207
+ },
2208
+ Flatten(path: "getT1s.@") {
2209
+ Fetch(service: "S1") {
2210
+ {
2211
+ ... on T1 {
2212
+ __typename
2213
+ id
2214
+ }
2215
+ } =>
2216
+ {
2217
+ ... on T1 {
2218
+ f1
2219
+ __typename
2220
+ id
2221
+ }
2222
+ }
2223
+ },
2224
+ },
2225
+ Flatten(path: "getT1s.@") {
2226
+ Fetch(service: "S2") {
2227
+ {
2228
+ ... on T1 {
2229
+ __typename
2230
+ id
2231
+ f1
2232
+ }
2233
+ } =>
2234
+ {
2235
+ ... on T1 {
2236
+ f2 {
2237
+ a
2238
+ }
2239
+ }
2240
+ }
2241
+ },
2242
+ },
2243
+ },
2244
+ }
2245
+ `);
2246
+ const response = await executeQueryPlan(queryPlan, serviceMap, buildRequestContext(), operationContext);
2247
+ expect(response.data).toMatchInlineSnapshot(`
2248
+ Object {
2249
+ "getT1s": Array [
2250
+ Object {
2251
+ "f1": "foo",
2252
+ "f2": Object {
2253
+ "a": "t1:foo",
2254
+ },
2255
+ "id": 0,
2256
+ },
2257
+ Object {
2258
+ "f1": null,
2259
+ "f2": null,
2260
+ "id": 1,
2261
+ },
2262
+ Object {
2263
+ "f1": "bar",
2264
+ "f2": Object {
2265
+ "a": "t1:bar",
2266
+ },
2267
+ "id": 2,
2268
+ },
2269
+ ],
2270
+ }
2271
+ `);
2272
+ expect(response.errors?.map((e) => e.message)).toStrictEqual(['String cannot represent value: ["invalid"]']);
2273
+ });
2274
+ });
1639
2275
  });
@@ -2,7 +2,7 @@ import {
2
2
  GraphQLSchemaValidationError,
3
3
  GraphQLSchemaModule,
4
4
  GraphQLResolverMap,
5
- } from 'apollo-graphql';
5
+ } from '../schema-helper';
6
6
  import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
7
7
  import {
8
8
  composeAndValidate,
@@ -117,8 +117,8 @@ export function getTestingSupergraphSdl(services: typeof fixtures = fixtures) {
117
117
  );
118
118
  }
119
119
 
120
- export function wait(ms: number) {
121
- return new Promise(r => setTimeout(r, ms));
120
+ export function wait(ms: number, toResolveTo?: any) {
121
+ return new Promise((r) => setTimeout(() => r(toResolveTo), ms));
122
122
  }
123
123
 
124
124
  export function printPlan(queryPlan: QueryPlan): string {
@@ -1,4 +1,4 @@
1
- import { fetch } from '../../__mocks__/apollo-server-env';
1
+ import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
2
2
  import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
3
3
 
4
4
  import { RemoteGraphQLDataSource } from '../../datasources/RemoteGraphQLDataSource';
@@ -218,7 +218,7 @@ it('does not share service definition cache between gateways', async () => {
218
218
  url: 'https://api.example.com/repeat',
219
219
  },
220
220
  ],
221
- experimental_didUpdateComposition: updateObserver,
221
+ experimental_didUpdateSupergraph: updateObserver,
222
222
  });
223
223
 
224
224
  await gateway.load();
@@ -237,7 +237,7 @@ it('does not share service definition cache between gateways', async () => {
237
237
  url: 'https://api.example.com/repeat',
238
238
  },
239
239
  ],
240
- experimental_didUpdateComposition: updateObserver,
240
+ experimental_didUpdateSupergraph: updateObserver,
241
241
  });
242
242
 
243
243
  await gateway.load();
@@ -1,10 +1,10 @@
1
- import { GraphQLSchemaModule } from 'apollo-graphql';
2
1
  import { buildSubgraphSchema } from '@apollo/subgraph';
3
2
  import { ApolloServer } from 'apollo-server';
4
3
  import fetch from 'node-fetch';
5
4
  import { ApolloGateway } from '../..';
6
5
  import { fixtures } from 'apollo-federation-integration-testsuite';
7
6
  import { ApolloServerPluginInlineTrace } from 'apollo-server-core';
7
+ import { GraphQLSchemaModule } from '../../schema-helper';
8
8
 
9
9
  async function startFederatedServer(modules: GraphQLSchemaModule[]) {
10
10
  const schema = buildSubgraphSchema(modules);
@@ -1,8 +1,8 @@
1
+ import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
1
2
  import gql from 'graphql-tag';
2
3
  import { ApolloGateway } from '../../';
3
4
  import { fixtures } from 'apollo-federation-integration-testsuite';
4
5
  import { Logger } from 'apollo-server-types';
5
- import { fetch } from '../../__mocks__/apollo-server-env';
6
6
 
7
7
  let logger: {
8
8
  warn: jest.MockedFunction<Logger['warn']>,