Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Jan 9, 2025
1 parent 5334f30 commit 04c03c7
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 101 deletions.
14 changes: 9 additions & 5 deletions e2e/polling/polling.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import { createTenv } from '@internal/e2e';
import { describe, expect, it } from 'vitest';

describe('Polling', async () => {
const { service, gateway } = createTenv(__dirname);
const { service, gateway, composeWithMesh } = createTenv(__dirname);
const { output } = await composeWithMesh({
services: [await service('Graph')],
output: 'graphql',
});
const gw = await gateway({
supergraph: {
with: 'mesh',
services: [await service('Graph')],
args: ['supergraph'],
env: {
SUPERGRAPH_PATH: output,
},
});
it('should not break the long running query while polling and schema remaining the same', async () => {
Expand All @@ -17,7 +21,7 @@ describe('Polling', async () => {
}
`,
});
expect(res).toMatchObject({
expect(res).toEqual({
data: {
hello: 'Hello world!',
},
Expand Down
1 change: 0 additions & 1 deletion packages/fusion-runtime/src/unifiedGraphManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,6 @@ export class UnifiedGraphManager<TContext> implements AsyncDisposable {
this._transportExecutorStack.defer(() => {
this.cleanup();
});
this.lastLoadedUnifiedGraph ||= loadedUnifiedGraph;
this.lastLoadedUnifiedGraph = loadedUnifiedGraph;
this.unifiedGraph = ensureSchema(loadedUnifiedGraph);
const {
Expand Down
18 changes: 9 additions & 9 deletions packages/fusion-runtime/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type {
Transport,
TransportContext,
TransportEntry,
TransportGetSubgraphExecutor,
TransportGetSubgraphExecutorOptions,
import {
defaultPrintFn,
type Transport,
type TransportContext,
type TransportEntry,
type TransportGetSubgraphExecutor,
type TransportGetSubgraphExecutorOptions,
} from '@graphql-mesh/transport-common';
import type { Logger } from '@graphql-mesh/types';
import {
Expand Down Expand Up @@ -31,7 +32,6 @@ import {
import { constantCase } from 'constant-case';
import {
FragmentDefinitionNode,
print,
SelectionNode,
SelectionSetNode,
type DocumentNode,
Expand Down Expand Up @@ -494,15 +494,15 @@ export function compareSchemas(
if (typeof a === 'string') {
aStr = a;
} else if (isDocumentNode(a)) {
aStr = print(a);
aStr = defaultPrintFn(a);
} else {
aStr = printSchemaWithDirectives(a);
}
let bStr: string;
if (typeof b === 'string') {
bStr = b;
} else if (isDocumentNode(b)) {
bStr = print(b);
bStr = defaultPrintFn(b);
} else {
bStr = printSchemaWithDirectives(b);
}
Expand Down
71 changes: 52 additions & 19 deletions packages/runtime/src/createGatewayRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type {
OnDelegationStageExecuteHook,
OnSubgraphExecuteHook,
TransportEntry,
UnifiedGraphManagerOptions,
} from '@graphql-mesh/fusion-runtime';
import {
getOnSubgraphExecute,
Expand All @@ -36,6 +35,7 @@ import {
delegateToSchema,
type SubschemaConfig,
} from '@graphql-tools/delegate';
import { defaultPrintFn } from '@graphql-tools/executor-common';
import { fetchSupergraphSdlFromManagedFederation } from '@graphql-tools/federation';
import {
asArray,
Expand All @@ -46,6 +46,7 @@ import {
mapMaybePromise,
mergeDeep,
parseSelectionSet,
printSchemaWithDirectives,
type Executor,
type MaybePromise,
type TypeSource,
Expand All @@ -59,6 +60,7 @@ import {
DisposableSymbols,
} from '@whatwg-node/disposablestack';
import {
buildASTSchema,
buildSchema,
GraphQLSchema,
isSchema,
Expand All @@ -78,8 +80,8 @@ import type { GraphiQLOptions, PromiseOrValue } from 'graphql-yoga';
import { getProxyExecutor } from './getProxyExecutor';
import { getReportingPlugin } from './getReportingPlugin';
import {
getUnifiedGraphSDL,
handleUnifiedGraphConfig,
UnifiedGraphSchema,
} from './handleUnifiedGraphConfig';
import landingPageHtml from './landing-page-html';
import { useChangingSchema } from './plugins/useChangingSchema';
Expand Down Expand Up @@ -294,7 +296,21 @@ export function createGatewayRuntime<
configContext,
),
(schema) => {
setSchema(schema);
if (isSchema(schema)) {
unifiedGraph = schema;
} else if (isDocumentNode(schema)) {
unifiedGraph = buildASTSchema(schema, {
assumeValid: true,
assumeValidSDL: true,
});
} else {
unifiedGraph = buildSchema(schema, {
noLocation: true,
assumeValid: true,
assumeValidSDL: true,
});
}
setSchema(unifiedGraph);
continuePolling();
return true;
},
Expand Down Expand Up @@ -403,7 +419,20 @@ export function createGatewayRuntime<
getSubschemaConfig$ = mapMaybePromise(
handleUnifiedGraphConfig(subgraphInConfig, configContext),
(newUnifiedGraph) => {
unifiedGraph = newUnifiedGraph;
if (isSchema(newUnifiedGraph)) {
unifiedGraph = newUnifiedGraph;
} else if (isDocumentNode(newUnifiedGraph)) {
unifiedGraph = buildASTSchema(newUnifiedGraph, {
assumeValid: true,
assumeValidSDL: true,
});
} else {
unifiedGraph = buildSchema(newUnifiedGraph, {
noLocation: true,
assumeValid: true,
assumeValidSDL: true,
});
}
unifiedGraph = restoreExtraDirectives(unifiedGraph);
subschemaConfig = {
name: getDirectiveExtensions(unifiedGraph)?.['transport']?.[0]?.[
Expand Down Expand Up @@ -536,7 +565,13 @@ export function createGatewayRuntime<
_service() {
return {
sdl() {
return getUnifiedGraphSDL(newUnifiedGraph);
if (isSchema(newUnifiedGraph)) {
return printSchemaWithDirectives(newUnifiedGraph);
}
if (isDocumentNode(newUnifiedGraph)) {
return defaultPrintFn(newUnifiedGraph);
}
return newUnifiedGraph;
},
};
},
Expand Down Expand Up @@ -578,7 +613,7 @@ export function createGatewayRuntime<
},
};
} /** 'supergraph' in config */ else {
let unifiedGraphFetcher: UnifiedGraphManagerOptions<unknown>['getUnifiedGraph'];
let unifiedGraphFetcher: () => MaybePromise<UnifiedGraphSchema>;
let supergraphLoadedPlace: string;

if (typeof config.supergraph === 'object' && 'type' in config.supergraph) {
Expand Down Expand Up @@ -659,7 +694,6 @@ export function createGatewayRuntime<
}
} else {
// local or remote

if (!isDynamicUnifiedGraphSchema(config.supergraph)) {
// no polling for static schemas
logger.debug(`Disabling polling for static supergraph`);
Expand All @@ -671,11 +705,8 @@ export function createGatewayRuntime<
}

unifiedGraphFetcher = () =>
handleUnifiedGraphConfig(
// @ts-expect-error TODO: what's up with type narrowing
config.supergraph,
configContext,
);
// @ts-expect-error TODO: what's up with type narrowing
handleUnifiedGraphConfig(config.supergraph, configContext);
if (typeof config.supergraph === 'function') {
const fnName = config.supergraph.name || '';
supergraphLoadedPlace = `a custom loader ${fnName}`;
Expand Down Expand Up @@ -1098,12 +1129,14 @@ function isDynamicUnifiedGraphSchema(
return false;
}
if (typeof schema === 'string') {
try {
// sdl
parse(schema);
return false;
} catch {}
if (isValidPath(schema)) {
// local file path
return true;
}
if (isUrl(schema)) {
// remote url
return true;
}
}
// likely a dynamic schema that can be polled
return true;
return false;
}
97 changes: 30 additions & 67 deletions packages/runtime/src/handleUnifiedGraphConfig.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,61 @@
import { UnifiedGraphManagerOptions } from '@graphql-mesh/fusion-runtime';
import { defaultImportFn, isUrl, readFileOrUrl } from '@graphql-mesh/utils';
import { defaultPrintFn } from '@graphql-tools/executor-common';
import type { MaybePromise } from '@graphql-tools/utils';
import {
getDocumentNodeFromSchema,
isDocumentNode,
isValidPath,
mapMaybePromise,
printSchemaWithDirectives,
} from '@graphql-tools/utils';
import type { DocumentNode, GraphQLSchema } from 'graphql';
import { buildASTSchema, isSchema, parse, print } from 'graphql';
import { isSchema } from 'graphql';
import type { GatewayConfigContext } from './types';

export type UnifiedGraphSchema = GraphQLSchema | DocumentNode | string;
export type UnifiedGraphSchema = Awaited<
ReturnType<UnifiedGraphManagerOptions<unknown>['getUnifiedGraph']>
>;

export type UnifiedGraphConfig =
| UnifiedGraphSchema
| Promise<UnifiedGraphSchema>
| (() => UnifiedGraphSchema | Promise<UnifiedGraphSchema>);
| ((
configContext: GatewayConfigContext,
) => UnifiedGraphSchema | Promise<UnifiedGraphSchema>);

export function handleUnifiedGraphConfig(
config: UnifiedGraphConfig,
configContext: GatewayConfigContext,
): MaybePromise<GraphQLSchema> {
): MaybePromise<UnifiedGraphSchema> {
return mapMaybePromise(
typeof config === 'function' ? config() : config,
typeof config === 'function' ? config(configContext) : config,
(schema) => handleUnifiedGraphSchema(schema, configContext),
);
}

export const unifiedGraphASTMap = new WeakMap<GraphQLSchema, DocumentNode>();
export const unifiedGraphSDLMap = new WeakMap<GraphQLSchema, string>();

export function getUnifiedGraphSDL(schema: GraphQLSchema) {
let sdl = unifiedGraphSDLMap.get(schema);
if (!sdl) {
const ast = getUnifiedGraphAST(schema);
sdl = print(ast);
unifiedGraphSDLMap.set(schema, sdl);
}
return sdl;
}

export function getUnifiedGraphAST(schema: GraphQLSchema) {
let ast = unifiedGraphASTMap.get(schema);
if (!ast) {
ast = getDocumentNodeFromSchema(schema);
unifiedGraphASTMap.set(schema, ast);
export function getUnifiedGraphSDL(unifiedGraphSchema: UnifiedGraphSchema) {
if (isSchema(unifiedGraphSchema)) {
return printSchemaWithDirectives(unifiedGraphSchema);
} else if (isDocumentNode(unifiedGraphSchema)) {
return defaultPrintFn(unifiedGraphSchema);
}
return ast;
return unifiedGraphSchema;
}

export function handleUnifiedGraphSchema(
unifiedGraphSchema: UnifiedGraphSchema,
configContext: GatewayConfigContext,
): Promise<GraphQLSchema> | GraphQLSchema {
if (isSchema(unifiedGraphSchema)) {
return unifiedGraphSchema;
}
if (isDocumentNode(unifiedGraphSchema)) {
const schema = buildASTSchema(unifiedGraphSchema, {
assumeValid: true,
assumeValidSDL: true,
): MaybePromise<UnifiedGraphSchema> {
if (
typeof unifiedGraphSchema === 'string' &&
(isValidPath(unifiedGraphSchema) || isUrl(unifiedGraphSchema))
) {
return readFileOrUrl<string>(unifiedGraphSchema, {
fetch: configContext.fetch,
cwd: configContext.cwd,
logger: configContext.logger,
allowUnknownExtensions: true,
importFn: defaultImportFn,
});
unifiedGraphASTMap.set(schema, unifiedGraphSchema);
return schema;
}
if (typeof unifiedGraphSchema === 'string') {
if (isValidPath(unifiedGraphSchema) || isUrl(unifiedGraphSchema)) {
return readFileOrUrl<string>(unifiedGraphSchema, {
fetch: configContext.fetch,
cwd: configContext.cwd,
logger: configContext.logger,
allowUnknownExtensions: true,
importFn: defaultImportFn,
}).then((sdl) => handleUnifiedGraphSchema(sdl, configContext));
}
try {
const ast = parse(unifiedGraphSchema, {
noLocation: true,
});
const schema = buildASTSchema(ast, {
assumeValid: true,
assumeValidSDL: true,
});
unifiedGraphSDLMap.set(schema, unifiedGraphSchema);
unifiedGraphASTMap.set(schema, ast);
return schema;
} catch (e) {
configContext.logger.error(
`Failed to build UnifiedGraphSchema from "${unifiedGraphSchema}"`,
);
throw e;
}
}
throw new Error(
`Invalid UnifiedGraphSchema "${unifiedGraphSchema}". It can be an SDL string, an instance of GraphQLSchema or DocumentNode, or a function that returns/resolves any of these.`,
);
return unifiedGraphSchema;
}

0 comments on commit 04c03c7

Please sign in to comment.