graphql#buildSchema TypeScript Examples
The following examples show how to use
graphql#buildSchema.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts From graphql-query-generator with MIT License | 6 votes |
export function getGitHubQueryGenerator(gitHubAccessToken: string) {
return new Promise<GitHubQueryGenerator>((resolve, reject) => {
const gitHubSchemaStr = fs.readFileSync(
path.resolve(__dirname, '../fixtures/github.graphql'),
'utf8'
)
const gitHubSchema = buildSchema(gitHubSchemaStr)
getProviderMap(gitHubAccessToken).then((gitHubProviders) => {
resolve(
new GitHubQueryGenerator(gitHubSchema, {
breadthProbability: 0.5,
depthProbability: 0.5,
maxDepth: 10,
providerMap: gitHubProviders,
argumentsToConsider: ['first'],
considerUnions: true,
pickNestedQueryField: true
})
)
})
})
}
Example #2
Source File: typeCase.ts From amplify-codegen with Apache License 2.0 | 6 votes |
animalSchema = buildSchema(`
type Query {
animal: Animal
catOrBird: CatOrBird
}
union Animal = Cat | Bird | Crocodile | Fish
union CatOrBird = Cat | Bird
interface Pet {
name: String!
}
interface WarmBlooded {
bodyTemperature: Int!
}
type Cat implements Pet & WarmBlooded {
name: String!
bodyTemperature: Int!
}
type Bird implements Pet & WarmBlooded {
name: String!
bodyTemperature: Int!
}
type Fish implements Pet {
name: String!
}
type Crocodile {
age: Int!
}
`)
Example #3
Source File: GraphQlDefinition.tsx From backstage with Apache License 2.0 | 6 votes |
GraphQlDefinition = ({ definition }: Props) => {
const classes = useStyles();
const schema = buildSchema(definition);
return (
<div className={classes.root}>
<div className={classes.graphiQlWrapper}>
<GraphiQL
fetcher={() => Promise.resolve(null) as any}
schema={schema}
docExplorerOpen
defaultSecondaryEditorOpen={false}
/>
</div>
</div>
);
}
Example #4
Source File: index.ts From mercurius-typescript with MIT License | 6 votes |
{ schema } = loadSchemaFiles('src/graphql/schema/**/*.gql', {
watchOptions: {
enabled: process.env.NODE_ENV === 'development',
onChange(schema) {
app.graphql.replaceSchema(buildSchema(schema.join('\n')))
app.graphql.defineResolvers(resolvers)
codegenMercurius(app, {
targetPath: './src/graphql/generated.ts',
operationsGlob: './src/graphql/operations/*.gql',
}).catch(console.error)
},
},
})
Example #5
Source File: graphql.ts From react-js-tutorial with MIT License | 6 votes |
schema = buildSchema(`
type Query {
course(id: Int!): Course
courses(topic: String): [Course]
},
type Course {
id: Int
topic: String
title: String
author: String
description: String
url: String
}
`)
Example #6
Source File: render.ts From genql with MIT License | 6 votes |
toClientSchema = async (schemaGql: string) => {
const schema = buildSchema(schemaGql)
const introspectionResponse = await graphql(
schema,
getIntrospectionQuery(),
)
if (!introspectionResponse.data) {
throw new Error(JSON.stringify(introspectionResponse.errors))
}
return buildClientSchema(introspectionResponse.data as any)
}
Example #7
Source File: consider-listsize-directive.test.ts From graphql-query-generator with MIT License | 6 votes |
test(`@listSize with assumedSize and required argument`, () => {
const schema = buildSchema(`
directive @listSize(requireOneSlicingArgument: Boolean = true, assumedSize: Int, slicingArguments: [String], sizedFields: [String]) on FIELD_DEFINITION
type Order {
id: ID
date: String
}
type Query {
orders(first: Int!, after: ID, last: Int, before: ID): [Order] @listSize(assumedSize: 10)
}
`)
const config = {
providePlaceholders: true,
seed: 1
}
const { queryDocument, variableValues } = generateRandomQuery(schema, config)
const query = print(queryDocument)
expect(query.trim()).toEqual(
dedent(`
query RandomQuery($Query__orders__first: Int!) {
orders(first: $Query__orders__first) {
id
date
}
}
`).trim()
)
const variables = JSON.stringify(variableValues, null, 2)
expect(variables.trim()).toEqual(
dedent(`
{
"Query__orders__first": 10
}
`).trim()
)
expect(validate(schema, queryDocument)).toEqual([])
})
Example #8
Source File: index.ts From nodejs-typescript-graphql-starter with MIT License | 6 votes |
graphQlSchema = buildSchema(`
scalar Date
type User {
_id: ID!
name: String
email: String!
password: String!
salt: String!
role: String!
lastLogin: Date
createdAt: Date
updatedAt: Date
}
type RootQuery {
users: [User]
}
schema {
query: RootQuery
}
`)
Example #9
Source File: consider-listsize-directive.test.ts From graphql-query-generator with MIT License | 6 votes |
test(`Ignore slicing argument if requireOneSlicingArgument is set to false`, () => {
const schema = buildSchema(`
directive @listSize(requireOneSlicingArgument: Boolean = true, assumedSize: Int, slicingArguments: [String], sizedFields: [String]) on FIELD_DEFINITION
type Order {
id: ID
date: String
}
type Query {
orders(first: Int, after: ID, last: Int, before: ID): [Order] @listSize(slicingArguments: ["first"], requireOneSlicingArgument: false)
}
`)
const { queryDocument } = generateRandomQuery(schema, { seed: 1 })
const query = print(queryDocument)
expect(query.trim()).toEqual(
dedent(`
query RandomQuery {
orders {
id
date
}
}
`).trim()
)
expect(validate(schema, queryDocument)).toEqual([])
})
Example #10
Source File: appsync-javascript-visitor.test.ts From amplify-codegen with Apache License 2.0 | 5 votes |
buildSchemaWithDirectives = (schema: String): GraphQLSchema => {
return buildSchema([schema, directives, scalars].join('\n'));
}
Example #11
Source File: testGenerateQueries.ts From genql with MIT License | 5 votes |
schema = buildSchema(`
interface Node {
id: ID
}
enum Choice {
ONE,
TWO,
THREE
}
type User implements Node {
name: String
company(id: String): Company
employerCompany: Company
pastEmployers(max: Int! = 1): [Company]
id: ID
}
type DirectorConnection {
ceos: [User],
cursor: ID
}
type Nested {
user: User
}
type Company implements Node {
name: String
nested: Nested
legalForm: String
ceo: User
id: ID
employees(limit: Int! = 1): [User]
directors(limit: Int! = 1): DirectorConnection,
choice: Choice
}
type Query {
user(username: String!, choice: Choice!): User
users(limit: Int! = 1, first: Int = 1, last: Int = 1): [User]
company(id: String!, max: Int! = 1): Company
node(id: ID): Node
other(_id: ID!): String
}
schema {
query: Query
}`)
Example #12
Source File: appsync-json-metadata-visitor.test.ts From amplify-codegen with Apache License 2.0 | 5 votes |
buildSchemaWithDirectives = (schema: String): GraphQLSchema => {
return buildSchema([schema, directives, scalars].join('\n'));
}
Example #13
Source File: Variables.test.ts From tql with MIT License | 5 votes |
describe("Variables", () => {
it("builds variable definitions", () => {
const schema = buildSchema(
`
type Query {
hello(name: String!): String!
}
`,
{ noLocation: true }
);
const selection = new Selection(schema, "Query", [
field("hello", [argument("name", variable("name"))]),
]);
// @note we need a way to get the input type at runtime
const variableDefinitions = buildVariableDefinitions(
schema,
OperationTypeNode.QUERY,
selectionSet(selection)
);
expect(variableDefinitions).toEqual([
variableDefinition(variable("name"), nonNull(namedType("String"))),
]);
});
it("infers nested variable definitions", () => {
const schema = buildSchema(
`
type Query {
viewer: User!
}
type User {
id: ID!
friends(limit: Int!): [User!]
}
`,
{ noLocation: true }
);
const selection = new Selection(schema, "Query", [
field(
"viewer",
undefined,
selectionSet([
field(
"friends",
[argument("limit", variable("friendsLimit"))],
selectionSet([field("id")])
),
])
),
]);
const variableDefinitions = buildVariableDefinitions(
schema,
OperationTypeNode.QUERY,
selectionSet(selection)
);
expect(variableDefinitions).toEqual([
variableDefinition(variable("friendsLimit"), nonNull(namedType("Int"))),
]);
});
});
Example #14
Source File: index.ts From graphql-typed-document-node with MIT License | 5 votes |
schema = buildSchema(readFileSync('./schema.graphql', 'utf-8'))
Example #15
Source File: index.test.ts From hono with MIT License | 5 votes |
describe('GraphQL Middleware - Simple way', () => {
// Construct a schema, using GraphQL schema language
const schema = buildSchema(`
type Query {
hello: String
}
`)
// The root provides a resolver function for each API endpoint
const rootValue = {
hello: () => 'Hello world!',
}
const app = new Hono()
app.use(
'/graphql',
graphqlServer({
schema,
rootValue,
})
)
app.all('*', (c) => {
c.header('foo', 'bar')
return c.text('fallback')
})
it('Should return GraphQL response', async () => {
const query = 'query { hello }'
const body = {
query: query,
}
const res = await app.request('http://localhost/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
expect(res).not.toBeNull()
expect(res.status).toBe(200)
expect(await res.text()).toBe('{"data":{"hello":"Hello world!"}}')
expect(res.headers.get('foo')).toBeNull() // GraphQL Server middleware should be Handler
})
})
Example #16
Source File: schema.ts From Deep-Lynx with MIT License | 5 votes |
schema = buildSchema(generateSchema())
Example #17
Source File: generated.tsx From genql with MIT License | 5 votes |
schema = buildSchema(`
interface Node {
id: ID
}
enum Choice {
ONE,
TWO,
THREE
}
type User implements Node {
name: String
company(id: String): Company
employerCompany: Company
pastEmployers(max: Int! = 1): [Company]
id: ID
}
type DirectorConnection {
ceos: [User],
cursor: ID
}
type Nested {
user: User
}
type Company implements Node {
name: String
nested: Nested
legalForm: String
ceo: User
id: ID
employees(limit: Int! = 1): [User]
directors(limit: Int! = 1): DirectorConnection,
choice: Choice
}
type Query {
user(username: String!, choice: Choice!): User
users(limit: Int! = 1, first: Int = 1, last: Int = 1): [User]
company(id: String!, max: Int! = 1): Company
node(id: ID): Node
other(_id: ID!): String
}
schema {
query: Query
}`)
Example #18
Source File: graphql-server.ts From graphql-eslint with MIT License | 5 votes |
graphqlSchemaObj = buildSchema(sdlSchema)
Example #19
Source File: render.ts From tql with MIT License | 5 votes |
render = (sdl: string): string => {
const ast = parse(sdl, { noLocation: true });
const schema = buildSchema(sdl);
const transforms = [
typeTransform(ast, schema),
selectorInterfaceTransform(ast, schema),
];
// additive transforms
const results = transforms.map(
(vistor) => visit(ast, vistor)
) as unknown as Array<{ readonly definitions: Code[] }> ;
const types = Object.values(schema.getTypeMap()).filter(
(type) => !type.name.startsWith("__")
);
const enumValues = new Set(
Object.values(schema.getTypeMap())
.filter((type) => type instanceof GraphQLEnumType)
.flatMap((type) =>
(type as GraphQLEnumType).getValues().map((value) => value.value)
)
);
const ENUMS = `
Object.freeze({
${Array.from(enumValues)
.map((value) => `${value}: true`)
.join(",\n")}
} as const)
`;
const typeMap = `
export interface ISchema {
${types.map(printType).join("\n")}
}
`;
const source =
`
import { buildASTSchema, Kind, OperationTypeNode } from 'graphql'
import {
TypeConditionError,
NamedType,
Field,
InlineFragment,
Argument,
Variable,
Selection,
SelectionSet,
SelectionBuilder,
namedType,
field,
inlineFragment,
argument,
selectionSet
} from '@timkendall/tql'
export type { Result, SelectionResult, Variables } from '@timkendall/tql'
export { $ } from '@timkendall/tql'
` +
`
export const SCHEMA = buildASTSchema(${stringifyAST(ast)})
export const ENUMS = ${ENUMS}
${typeMap}
` +
results
.flatMap((result) =>
result.definitions.map((code) => code.toCodeString())
)
.join("\n");
return prettier.format(source, { parser: "typescript" });
}
Example #20
Source File: index.ts From graphql-mesh with MIT License | 5 votes |
private async replaceFederationSDLWithStitchingSDL(
name: string,
oldSchema: GraphQLSchema,
executor: Executor,
stitchingDirectives: StitchingDirectivesResult
) {
const rawSourceLogger = this.logger.child(name);
rawSourceLogger.debug(`Extracting existing resolvers if available`);
const resolvers = extractResolvers(oldSchema);
let newSchema = await this.store
.proxy(`${name}_stitching`, PredefinedProxyOptions.GraphQLSchemaWithDiffing)
.getWithSet(async () => {
this.logger.debug(`Fetching Apollo Federated Service SDL for ${name}`);
const sdlQueryResult: any = (await executor({
document: parse(APOLLO_GET_SERVICE_DEFINITION_QUERY),
})) as ExecutionResult;
if (sdlQueryResult.errors?.length) {
throw new AggregateError(sdlQueryResult.errors, `Failed on fetching Federated SDL for ${name}`);
}
const federationSdl = sdlQueryResult.data._service.sdl;
this.logger.debug(`Generating Stitching SDL for ${name}`);
const stitchingSdl = federationToStitchingSDL(federationSdl, stitchingDirectives);
return buildSchema(stitchingSdl, {
assumeValid: true,
assumeValidSDL: true,
});
});
rawSourceLogger.debug(`Adding existing resolvers back to the schema`);
newSchema = addResolversToSchema({
schema: newSchema,
resolvers,
updateResolversInPlace: true,
resolverValidationOptions: {
requireResolversToMatchSchema: 'ignore',
},
});
return newSchema;
}
Example #21
Source File: consider-listsize-directive.test.ts From graphql-query-generator with MIT License | 5 votes |
test(`Add nested slicing argument ("args.first") when defined in @listSize`, () => {
const schema = buildSchema(`
directive @listSize(requireOneSlicingArgument: Boolean = true, assumedSize: Int, slicingArguments: [String], sizedFields: [String]) on FIELD_DEFINITION
input Args {
first: Int
after: ID
last: Int
before: ID
}
type Order {
id: ID
date: String
}
type Query {
orders(args: Args): [Order] @listSize(slicingArguments: ["args.first", "args.last"])
}
`)
const config = {
providePlaceholders: true,
seed: 1
}
const { queryDocument, variableValues } = generateRandomQuery(schema, config)
const query = print(queryDocument)
expect(query.trim()).toEqual(
dedent(`
query RandomQuery($Query__orders__args: Args) {
orders(args: $Query__orders__args) {
id
date
}
}
`).trim()
)
const variables = JSON.stringify(variableValues, null, 2)
expect(variables.trim()).toEqual(
dedent(`
{
"Query__orders__args": {
"first": 10
}
}
`).trim()
)
expect(validate(schema, queryDocument)).toEqual([])
})
Example #22
Source File: index.ts From graphql-mesh with MIT License | 5 votes |
async getNonExecutableSchemaForHTTPSource(
httpSourceConfig: YamlConfig.GraphQLHandlerHTTPConfiguration
): Promise<GraphQLSchema> {
const schemaHeadersFactory = getInterpolatedHeadersFactory(httpSourceConfig.schemaHeaders || {});
const customFetch = await this.getCustomFetchImpl(httpSourceConfig.customFetch);
if (httpSourceConfig.introspection) {
const headers = schemaHeadersFactory({
env: process.env,
});
const sdlOrIntrospection = await readFileOrUrl<string | IntrospectionQuery | DocumentNode>(
httpSourceConfig.introspection,
{
cwd: this.baseDir,
allowUnknownExtensions: true,
fetch: customFetch,
headers,
}
);
if (typeof sdlOrIntrospection === 'string') {
return buildSchema(sdlOrIntrospection);
} else if (isDocumentNode(sdlOrIntrospection)) {
return buildASTSchema(sdlOrIntrospection);
} else if (sdlOrIntrospection.__schema) {
return buildClientSchema(sdlOrIntrospection);
}
throw new Error(`Invalid introspection data: ${util.inspect(sdlOrIntrospection)}`);
}
return this.nonExecutableSchema.getWithSet(() => {
const endpointFactory = getInterpolatedStringFactory(httpSourceConfig.endpoint);
const executor = this.urlLoader.getExecutorAsync(httpSourceConfig.endpoint, {
...httpSourceConfig,
customFetch,
subscriptionsProtocol: httpSourceConfig.subscriptionsProtocol as SubscriptionProtocol,
});
return introspectSchema(function meshIntrospectionExecutor(params: ExecutionRequest) {
const resolverData = getResolverData(params);
return executor({
...params,
extensions: {
...params.extensions,
headers: schemaHeadersFactory(resolverData),
endpoint: endpointFactory(resolverData),
},
});
});
});
}
Example #23
Source File: appsync-java-visitor.test.ts From amplify-codegen with Apache License 2.0 | 5 votes |
buildSchemaWithDirectives = (schema: String): GraphQLSchema => {
return buildSchema([schema, directives, scalars].join('\n'));
}
Example #24
Source File: cache.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('cache', () => {
let schema: GraphQLSchema;
let cache: KeyValueCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
beforeEach(() => {
const baseSchema = buildSchema(/* GraphQL */ `
type Query {
user(id: ID!): User
users(filter: SearchUsersInput): [User!]!
}
type Mutation {
updateUser(userId: ID!, name: String!): User
deleteUser(userIdToDelete: ID!): Boolean
}
input SearchUsersInput {
username: String
email: String
name: String
age: String
}
type User {
id: ID!
username: String!
email: String!
profile: Profile!
friend(id: ID!): User
}
type Profile {
name: String!
age: Int!
}
`);
schema = addResolversToSchema({
schema: baseSchema,
resolvers: spies,
});
cache = new LocalforageCache();
pubsub = new PubSub();
spies.Query.user.mockClear();
spies.Query.users.mockClear();
});
describe('Resolvers Composition', () => {
it('should replace resolvers correctly with a specific type and field', async () => {
expect(schema.getQueryType()?.getFields().user.resolve.name).toBe(spies.Query.user.bind(null).name);
const transform = new CacheTransform({
apiName: 'test',
importFn,
cache,
config: [
{
field: 'Query.user',
},
],
pubsub,
baseDir,
});
const modifiedSchema = transform.transformSchema(schema);
expect(modifiedSchema!.getQueryType()?.getFields().user.resolve.name).not.toBe(spies.Query.user.bind(null).name);
expect(modifiedSchema!.getQueryType()?.getFields().users.resolve.name).toBe(spies.Query.users.bind(null).name);
});
it('should replace resolvers correctly with a wildcard', async () => {
expect(schema.getQueryType()?.getFields().user.resolve.name).toBe(spies.Query.user.bind(null).name);
expect(schema.getQueryType()?.getFields().users.resolve.name).toBe(spies.Query.users.bind(null).name);
const transform = new CacheTransform({
apiName: 'test',
importFn,
cache,
config: [
{
field: 'Query.*',
},
],
pubsub,
baseDir,
});
const modifiedSchema = transform.transformSchema(schema);
expect(modifiedSchema!.getQueryType()?.getFields().user.resolve.name).not.toBe(spies.Query.user.bind(null).name);
expect(modifiedSchema!.getQueryType()?.getFields().users.resolve.name).not.toBe(
spies.Query.users.bind(null).name
);
});
});
describe('Cache Wrapper', () => {
const checkCache = async (config: YamlConfig.CacheTransformConfig[], cacheKeyToCheck?: string) => {
const transform = new CacheTransform({
apiName: 'test',
importFn,
cache,
config,
pubsub,
baseDir,
});
const modifiedSchema = transform.transformSchema(schema);
const executeOptions = {
schema: modifiedSchema!,
document: parse(/* GraphQL */ `
query user {
user(id: 1) {
id
username
}
}
`),
contextValue: {},
};
const queryType = schema.getType('Query') as GraphQLObjectType;
const queryFields = queryType.getFields();
const operation = executeOptions.document.definitions[0] as OperationDefinitionNode;
const cacheKey =
cacheKeyToCheck ||
computeCacheKey({
keyStr: undefined,
args: { id: '1' },
info: {
fieldName: 'user',
parentType: queryType,
returnType: queryFields.user.type,
schema,
fieldNodes: operation.selectionSet.selections as FieldNode[],
fragments: {},
rootValue: {},
operation,
variableValues: {},
path: {
prev: null,
key: 'user',
},
} as any,
});
// No data in cache before calling it
expect(await cache.get(cacheKey)).not.toBeDefined();
// Run it for the first time
await execute(executeOptions);
// Original resolver should now be called
expect(spies.Query.user.mock.calls.length).toBe(1);
// Data should be stored in cache
const data: any = await cache.get(cacheKey);
const mockData = MOCK_DATA[0];
expect(data.id).toBe(mockData.id);
expect(data.username).toBe(mockData.username);
// Running it again
await execute(executeOptions);
// No new calls to the original resolver
expect(spies.Query.user.mock.calls.length).toBe(1);
return {
cache,
executeAgain: () => execute(executeOptions),
executeDocument: (operation: DocumentNode, variables: Record<string, any> = {}) =>
execute({
schema: modifiedSchema,
document: operation,
variableValues: variables,
}),
};
};
it('Should wrap resolver correctly with caching - without cacheKey', async () => {
await checkCache([
{
field: 'Query.user',
},
]);
});
it('Should wrap resolver correctly with caching with custom key', async () => {
const cacheKey = `customUser`;
await checkCache(
[
{
field: 'Query.user',
cacheKey,
},
],
cacheKey
);
});
it('Should wrap resolver correctly with caching with custom key', async () => {
const cacheKey = `customUser`;
await checkCache(
[
{
field: 'Query.user',
cacheKey,
},
],
cacheKey
);
});
it('Should clear cache correctly when TTL is set', async () => {
const key = 'user-1';
const { cache, executeAgain } = await checkCache(
[
{
field: 'Query.user',
cacheKey: key,
invalidate: {
ttl: 1,
},
},
],
key
);
expect(await cache.get(key)).toBeDefined();
await wait(1.1);
expect(await cache.get(key)).not.toBeDefined();
await executeAgain();
expect(await cache.get(key)).toBeDefined();
});
it('Should wrap resolver correctly with caching with custom calculated key - and ensure calling resovler again when key is different', async () => {
const { cache, executeDocument } = await checkCache(
[
{
field: 'Query.user',
cacheKey: `query-user-{args.id}`,
},
],
'query-user-1'
);
const otherIdQuery = parse(/* GraphQL */ `
query user {
user(id: 2) {
id
}
}
`);
expect(await cache.get('query-user-2')).not.toBeDefined();
await executeDocument(otherIdQuery);
const cachedObj: any = await cache.get('query-user-2');
const mockObj = MOCK_DATA[1];
expect(cachedObj.id).toBe(mockObj.id);
expect(spies.Query.user.mock.calls.length).toBe(2);
await executeDocument(otherIdQuery);
expect(spies.Query.user.mock.calls.length).toBe(2);
});
it('Should work correctly with argsHash', async () => {
const expectedHash = `query-user-${hashObject({ id: '1' })}`;
await checkCache(
[
{
field: 'Query.user',
cacheKey: `query-user-{argsHash}`,
},
],
expectedHash
);
});
it('Should work correctly with hash helper', async () => {
const expectedHash = hashObject('1');
await checkCache(
[
{
field: 'Query.user',
cacheKey: `{args.id|hash}`,
},
],
expectedHash
);
});
it('Should work correctly with date helper', async () => {
const expectedHash = `1-${dayjs(new Date()).format(`yyyy-MM-dd`)}`;
await checkCache(
[
{
field: 'Query.user',
cacheKey: `{args.id}-{yyyy-MM-dd|date}`,
},
],
expectedHash
);
});
});
describe('Opration-based invalidation', () => {
it('Should invalidate cache when mutation is done based on key', async () => {
const transform = new CacheTransform({
apiName: 'test',
importFn,
config: [
{
field: 'Query.user',
cacheKey: 'query-user-{args.id}',
invalidate: {
effectingOperations: [
{
operation: 'Mutation.updateUser',
matchKey: 'query-user-{args.userId}',
},
{
operation: 'Mutation.deleteUser',
matchKey: 'query-user-{args.userIdToDelete}',
},
],
},
},
],
cache,
pubsub,
baseDir,
});
const schemaWithCache = transform.transformSchema(schema);
const expectedCacheKey = `query-user-1`;
const executeOptions = {
schema: schemaWithCache!,
document: parse(/* GraphQL */ `
query user {
user(id: 1) {
id
name
}
}
`),
};
// Make sure cache works as needed, runs resolvers logic only once
expect(await cache.get(expectedCacheKey)).not.toBeDefined();
expect(spies.Query.user.mock.calls.length).toBe(0);
await execute(executeOptions);
expect(await cache.get(expectedCacheKey)).toBeDefined();
expect(spies.Query.user.mock.calls.length).toBe(1);
await execute(executeOptions);
expect(spies.Query.user.mock.calls.length).toBe(1);
// Run effecting mutation
await execute({
schema: schemaWithCache!,
document: parse(/* GraphQL */ `
mutation updateUser {
updateUser(userId: 1, name: "test new") {
id
name
}
}
`),
});
// Cache should be empty now, no calls for resolvers since then
expect(await cache.get(expectedCacheKey)).not.toBeDefined();
expect(spies.Query.user.mock.calls.length).toBe(1);
// Running again query, cache should be filled and resolver should get called again
await execute(executeOptions);
expect(await cache.get(expectedCacheKey)).toBeDefined();
expect(spies.Query.user.mock.calls.length).toBe(2);
});
describe('Subfields', () => {
it('Should cache queries including subfield arguments', async () => {
const transform = new CacheTransform({
apiName: 'test',
importFn,
config: [{ field: 'Query.user' }],
cache,
pubsub,
baseDir,
});
const schemaWithCache = transform.transformSchema(schema);
// First query should call resolver and fill cache
const executeOptions1 = {
schema: schemaWithCache,
document: parse(/* GraphQL */ `
query {
user(id: 1) {
friend(id: 2) {
id
}
}
}
`),
};
const { data: actual1 }: any = await execute(executeOptions1);
expect(spies.Query.user.mock.calls.length).toBe(1);
expect(actual1.user.friend.id).toBe('2');
// Second query should call resolver and also fill cache
const executeOptions2 = {
schema: schemaWithCache,
document: parse(/* GraphQL */ `
query {
user(id: 1) {
friend(id: 3) {
id
}
}
}
`),
};
const { data: actual2 }: any = await execute(executeOptions2);
expect(spies.Query.user.mock.calls.length).toBe(2);
expect(actual2.user.friend.id).toBe('3');
// Repeat both queries, no new calls for resolver
const { data: repeat1 }: any = await execute(executeOptions1);
const { data: repeat2 }: any = await execute(executeOptions2);
expect(spies.Query.user.mock.calls.length).toBe(2);
expect(repeat1.user.friend.id).toBe('2');
expect(repeat2.user.friend.id).toBe('3');
});
});
describe('Race condition', () => {
it('should wait for local cache transform to finish writing the entry', async () => {
const options: MeshTransformOptions<YamlConfig.CacheTransformConfig[]> = {
apiName: 'test',
importFn,
config: [
{
field: 'Query.foo',
cacheKey: 'random',
},
],
cache,
pubsub,
baseDir,
};
let callCount = 0;
const schema = makeExecutableSchema({
typeDefs: /* GraphQL */ `
type Query {
foo: String
}
`,
resolvers: {
Query: {
foo: () => new Promise(resolve => setTimeout(() => resolve((callCount++).toString()), 300)),
},
},
});
const transform = new CacheTransform(options);
const transformedSchema = transform.transformSchema(schema);
const query = /* GraphQL */ `
{
foo1: foo
foo2: foo
}
`;
const result = await execute({
schema: transformedSchema,
document: parse(query),
});
expect(result.data.foo2).toBe(result.data.foo1);
});
it('should wait for other cache transform to finish writing the entry when delay >= safe threshold)', async () => {
let callCount = 0;
const options: MeshTransformOptions<YamlConfig.CacheTransformConfig[]> = {
apiName: 'test',
importFn,
config: [
{
field: 'Query.foo',
cacheKey: 'random',
},
],
cache,
pubsub,
baseDir,
};
function getNewSchema() {
return makeExecutableSchema({
typeDefs: /* GraphQL */ `
type Query {
foo: String
}
`,
resolvers: {
Query: {
foo: async () => {
callCount++;
await new Promise(resolve => setTimeout(resolve, 300));
return 'FOO';
},
},
},
});
}
const transform1 = new CacheTransform(options);
const transformedSchema1 = transform1.transformSchema(getNewSchema());
const transform2 = new CacheTransform(options);
const transformedSchema2 = transform2.transformSchema(getNewSchema());
const query = /* GraphQL */ `
{
foo
}
`;
await execute({
schema: transformedSchema1,
document: parse(query),
});
await wait(0);
await execute({
schema: transformedSchema2,
document: parse(query),
});
expect(callCount).toBe(1);
});
it('should fail to wait for other cache transform to finish writing the entry when delay < safe threshold', async () => {
let callCount = 0;
const options: MeshTransformOptions<YamlConfig.CacheTransformConfig[]> = {
apiName: 'test',
importFn,
config: [
{
field: 'Query.foo',
cacheKey: 'random',
},
],
cache,
pubsub,
baseDir,
};
function getNewSchema() {
return makeExecutableSchema({
typeDefs: /* GraphQL */ `
type Query {
foo: String
}
`,
resolvers: {
Query: {
foo: async () => {
callCount++;
await new Promise(resolve => setTimeout(resolve, 300));
return 'FOO';
},
},
},
});
}
const transform1 = new CacheTransform(options);
const transformedSchema1 = transform1.transformSchema(getNewSchema());
const transform2 = new CacheTransform(options);
const transformedSchema2 = transform2.transformSchema(getNewSchema());
const query = /* GraphQL */ `
{
foo
}
`;
await Promise.all([
execute({
schema: transformedSchema1,
document: parse(query),
}),
execute({
schema: transformedSchema2,
document: parse(query),
}),
]);
expect(callCount).toBe(2);
});
});
});
});
Example #25
Source File: jsonOutput.ts From amplify-codegen with Apache License 2.0 | 4 votes |
describe('JSON output', function() {
test(`should generate JSON output for a query with an enum variable`, function() {
const context = compileFromSource(`
query HeroName($episode: Episode) {
hero(episode: $episode) {
name
}
}
`);
const output = serializeToJSON(context);
expect(output).toMatchSnapshot();
});
test(`should generate JSON output for a query with a nested selection set`, function() {
const context = compileFromSource(`
query HeroAndFriendsNames {
hero {
name
friends {
name
}
}
}
`);
const output = serializeToJSON(context);
expect(output).toMatchSnapshot();
});
test(`should generate JSON output for a query with a fragment spread and nested inline fragments`, function() {
const context = compileFromSource(`
query HeroAndDetails {
hero {
id
...CharacterDetails
}
}
fragment CharacterDetails on Character {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
`);
const output = serializeToJSON(context);
expect(output).toMatchSnapshot();
});
test(`should generate JSON output for a mutation with an enum and an input object variable`, function() {
const context = compileFromSource(`
mutation CreateReview($episode: Episode, $review: ReviewInput) {
createReview(episode: $episode, review: $review) {
stars
commentary
}
}
`);
const output = serializeToJSON(context);
expect(output).toMatchSnapshot();
});
test(`should generate JSON output for an input object type with default field values`, function() {
const schema = buildSchema(`
type Query {
someField(input: ComplexInput!): String!
}
input ComplexInput {
string: String = "Hello"
customScalar: Date = "2017-04-16"
listOfString: [String] = ["test1", "test2", "test3"]
listOfInt: [Int] = [1, 2, 3]
listOfEnums: [Episode] = [JEDI, EMPIRE]
listOfCustomScalar: [Date] = ["2017-04-16", "2017-04-17", "2017-04-18"]
}
scalar Date
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
`);
const context = compileFromSource(
`
query QueryWithComplexInput($input: ComplexInput) {
someField(input: $input)
}
`,
schema
);
const output = serializeToJSON(context);
expect(output).toMatchSnapshot();
});
});
Example #26
Source File: transform.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('filter', () => {
const cache = new InMemoryLRUCache();
const pubsub = new PubSub();
const baseDir: string = undefined;
const importFn: ImportFn = m => import(m);
it('filters correctly with array of rules', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
comments: [Comment]
}
type Comment {
id: ID
message: String
}
type Query {
user(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['!Comment', 'User.posts.{message, author}', 'Query.user.!pk'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Query {
user(name: String, age: Int): User
}
`.trim()
);
});
it('filters correctly with declarative syntax', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
comments: [Comment]
}
type Comment {
id: ID
message: String
}
type Query {
user(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: { filters: ['!Comment', 'User.posts.{message, author}', 'Query.user.!pk'] },
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Query {
user(name: String, age: Int): User
}
`.trim()
);
});
it("filters correctly on 'bare' mode", async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
notifications: [Notification]
mentions: [Mention]
}
type Post {
id: ID
message: String
author: User
comments: [Comment]
}
type Comment {
id: ID
message: String
}
type Notification {
type: Int
content: String
}
type Mention {
reference: ID
link: String
}
type LooseType {
foo: String
bar: String
}
type Query {
user(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: {
mode: 'bare',
filters: [
'!Comment',
'Type.!LooseType',
'Type.!{Notification, Mention}',
'Query.user.!{notifications, mentions}',
'User.posts.{message, author}',
'Query.user.!pk',
],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Query {
user(name: String, age: Int): User
}
`.trim()
);
});
it("filters correctly arguments on all fields in Type, with 'bare' mode", async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(pk: ID!, name: String, age: Int): User
userTwo(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: {
mode: 'bare',
filters: ['Query.*.!pk'],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(name: String, age: Int): User
userTwo(name: String, age: Int): User
}
`.trim()
);
});
it("filters correctly arguments on all fields in Type, plus specific field arguments; with 'bare' mode", async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(pk: ID!, name: String, age: Int): User
userTwo(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: {
mode: 'bare',
filters: ['Query.*.!pk', 'Query.userOne.!age'],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(name: String): User
userTwo(name: String, age: Int): User
}
`.trim()
);
});
it("filters correctly arguments on all fields in Type, with 'wrap' mode", async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(pk: ID!, name: String, age: Int): User
userTwo(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: {
mode: 'wrap',
filters: ['Query.*.!pk'],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(name: String, age: Int): User
userTwo(name: String, age: Int): User
}
`.trim()
);
});
it("filters correctly arguments on all fields in Type, plus specific field arguments; with 'wrap' mode", async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(pk: ID!, name: String, age: Int): User
userTwo(pk: ID!, name: String, age: Int): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: {
mode: 'wrap',
filters: ['Query.*.!pk', 'Query.userOne.!age'],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Query {
userOne(name: String): User
userTwo(name: String, age: Int): User
}
`.trim()
);
});
it('should filter out fields', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Book {
id: ID
name: String
authorId: ID
author: User
}
type Query {
user: User
admin: User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['User.!{a,b,c,d,e}', 'Query.!admin', 'Book.{id,name,author}'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Book {
id: ID
name: String
author: User
}
type Query {
user: User
}
`.trim()
);
});
it('should remove type with pruning if all fields are filtered out', async () => {
let schema = buildSchema(/* GraphQL */ `
type Query {
foo: String
bar: String
}
type Mutation {
baz: String
qux: String
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['Mutation.!*'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(pruneSchema(schema)).trim()).toBe(
/* GraphQL */ `
type Query {
foo: String
bar: String
}
`.trim()
);
});
it('should filter out fields if array syntax is used only with one element', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Book {
id: ID
name: String
authorId: ID
author: User
}
type Query {
user: User
admin: User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['User.{id, username}', 'Query.!{admin}', 'Book.{id}'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
username: String
}
type Book {
id: ID
}
type Query {
user: User
}
`.trim()
);
});
it('should filter out single type, with pending-deprecation syntax', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Book {
id: ID
name: String
authorId: ID
author: User
}
type Query {
user: User
admin: User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['!Book'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Query {
user: User
admin: User
}
`.trim()
);
});
it('filters out single type and multiple types rules', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Comment {
id: ID
message: String
}
type Notification {
type: Int
content: String
}
type Mention {
reference: ID
link: String
}
type Query {
user(id: ID!): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: { mode: 'bare', filters: ['Type.!Comment', 'Type.!{Notification, Mention}'] },
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Query {
user(id: ID!): User
}
`.trim()
);
});
it('handles whitelist filtering for types correctly', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Comment {
id: ID
message: String
}
type Notification {
type: Int
content: String
}
type Mention {
reference: ID
link: String
}
type Query {
user(id: ID!): User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
// bizarre case, but logic should still work
config: { mode: 'bare', filters: ['Type.{Query, User, Post, String, ID}'] },
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
posts: [Post]
}
type Post {
id: ID
message: String
author: User
}
type Query {
user(id: ID!): User
}
`.trim()
);
});
it('should filter out fields of filtered types', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Book {
id: ID
name: String
authorId: ID
author: User
}
type Query {
book: Book
user: User
admin: User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['!User'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
// TODO: temporary fix
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type Book {
id: ID
name: String
authorId: ID
}
type Query {
book: Book
}
`.trim()
);
});
it('should filter out directive fields of filtered types', async () => {
let schema = buildSchema(/* GraphQL */ `
input AuthRule {
and: [AuthRule]
or: [AuthRule]
not: AuthRule
rule: String
}
directive @auth(query: AuthRule, add: AuthRule, update: AuthRule, delete: AuthRule, role: String!) on OBJECT
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Book {
id: ID
name: String
authorId: ID
author: User
}
type Query {
user: User
admin: User
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['!AuthRule'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
directive @auth(role: String!) on OBJECT
type User {
id: ID
name: String
username: String
a: String
b: String
c: String
d: String
e: String
}
type Book {
id: ID
name: String
authorId: ID
author: User
}
type Query {
user: User
admin: User
}
`.trim()
);
});
it('should filter out arguments of root field', async () => {
let schema = buildSchema(/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Book {
id: ID
name: String
author: User
}
type Query {
user(pk: ID!, name: String, age: Int): User
book(pk: ID!, title: String): Book
}
`);
schema = wrapSchema({
schema,
transforms: [
FilterSchemaTransform({
config: ['Query.user.!{pk, age}', 'Query.book.title'],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
expect(printSchema(schema).trim()).toBe(
/* GraphQL */ `
type User {
id: ID
name: String
username: String
}
type Book {
id: ID
name: String
author: User
}
type Query {
user(name: String): User
book(title: String): Book
}
`.trim()
);
});
});
Example #27
Source File: typeCase.ts From amplify-codegen with Apache License 2.0 | 4 votes |
describe('TypeCase', () => {
it('should recursively include inline fragments with type conditions that match the parent type', () => {
const context = compile(`
query Hero {
hero {
id
... on Character {
name
... on Character {
id
appearsIn
}
id
}
}
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['id', 'name', 'appearsIn']);
expect(typeCase.variants).toHaveLength(0);
expect(typeCase.exhaustiveVariants).toHaveLength(1);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human', 'Droid'], ['id', 'name', 'appearsIn']);
});
it('should recursively include fragment spreads with type conditions that match the parent type', () => {
const context = compile(`
query Hero {
hero {
id
...HeroDetails
}
}
fragment HeroDetails on Character {
name
...MoreHeroDetails
id
}
fragment MoreHeroDetails on Character {
appearsIn
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['id', 'name', 'appearsIn']);
expect(typeCase.default.fragmentSpreads.map(fragmentSpread => fragmentSpread.fragmentName)).toEqual(['HeroDetails', 'MoreHeroDetails']);
expect(typeCase.variants).toHaveLength(0);
expect(typeCase.exhaustiveVariants).toHaveLength(1);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human', 'Droid'], ['id', 'name', 'appearsIn']);
});
it('should include fragment spreads when nested within inline fragments', () => {
const context = compile(`
query Hero {
hero {
... on Character {
...CharacterName
}
}
}
fragment CharacterName on Character {
name
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['name']);
expect(typeCase.default.fragmentSpreads.map(fragmentSpread => fragmentSpread.fragmentName)).toEqual(['CharacterName']);
expect(typeCase.variants).toHaveLength(0);
});
it('should only include fragment spreads once even if included twice in different subselections', () => {
const context = compile(`
query Hero {
hero {
friends {
...CharacterName
}
... on Droid {
friends {
...CharacterName
}
}
}
}
fragment CharacterName on Character {
name
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(collectAndMergeFields(typeCaseForSelectionSet(selectionSet).variants[0])[0]
.selectionSet as SelectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['name']);
expect(typeCase.default.fragmentSpreads.map(fragmentSpread => fragmentSpread.fragmentName)).toEqual(['CharacterName']);
});
it('should ignore type modifiers when matching the parent type', () => {
const schema = buildSchema(`
type Query {
heroes: [Character]
}
interface Character {
name: String!
}
type Human implements Character {
name: String!
}
type Droid implements Character {
name: String!
}
`);
const context = compile(
`
query Hero {
heroes {
... on Character {
name
}
}
}
`,
schema
);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['name']);
expect(typeCase.variants).toHaveLength(0);
expect(typeCase.exhaustiveVariants).toHaveLength(1);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human', 'Droid'], ['name']);
});
it('should merge fields from the default case into type conditions', () => {
const context = compile(`
query Hero {
hero {
name
... on Droid {
primaryFunction
}
appearsIn
... on Human {
height
}
}
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['name', 'appearsIn']);
expect(typeCase.variants).toHaveLength(2);
expect(typeCase.variants).toContainSelectionSetMatching(['Droid'], ['name', 'primaryFunction', 'appearsIn']);
expect(typeCase.variants).toContainSelectionSetMatching(['Human'], ['name', 'appearsIn', 'height']);
expect(typeCase.exhaustiveVariants).toHaveLength(2);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Droid'], ['name', 'primaryFunction', 'appearsIn']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human'], ['name', 'appearsIn', 'height']);
});
it(`should merge fields from type conditions with the same type`, () => {
const context = compile(`
query Hero {
hero {
name
... on Droid {
primaryFunction
}
... on Droid {
appearsIn
}
}
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], ['name']);
expect(typeCase.variants).toHaveLength(1);
expect(typeCase.variants).toContainSelectionSetMatching(['Droid'], ['name', 'primaryFunction', 'appearsIn']);
expect(typeCase.exhaustiveVariants).toHaveLength(2);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Droid'], ['name', 'primaryFunction', 'appearsIn']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human'], ['name']);
});
it('should inherit type condition when nesting an inline fragment in an inline fragment with a more specific type condition', () => {
const context = compile(`
query Hero {
hero {
... on Droid {
... on Character {
name
}
}
}
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], []);
expect(typeCase.variants).toHaveLength(1);
expect(typeCase.variants).toContainSelectionSetMatching(['Droid'], ['name']);
expect(typeCase.exhaustiveVariants).toHaveLength(2);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Droid'], ['name']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human'], []);
});
it('should not inherit type condition when nesting an inline fragment in an inline fragment with a less specific type condition', () => {
const context = compile(`
query Hero {
hero {
... on Character {
... on Droid {
name
}
}
}
}
`);
const selectionSet = (context.operations['Hero'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Human', 'Droid'], []);
expect(typeCase.variants).toHaveLength(1);
expect(typeCase.variants).toContainSelectionSetMatching(['Droid'], ['name']);
expect(typeCase.exhaustiveVariants).toHaveLength(2);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Droid'], ['name']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Human'], []);
});
it('should merge fields from the parent case into nested type conditions', () => {
const context = compile(
`
query Animal {
animal {
... on Pet {
name
... on WarmBlooded {
bodyTemperature
}
}
}
}
`,
animalSchema
);
const selectionSet = (context.operations['Animal'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Cat', 'Bird', 'Fish', 'Crocodile'], []);
expect(typeCase.variants).toHaveLength(2);
expect(typeCase.variants).toContainSelectionSetMatching(['Cat', 'Bird'], ['name', 'bodyTemperature']);
expect(typeCase.variants).toContainSelectionSetMatching(['Fish'], ['name']);
expect(typeCase.exhaustiveVariants).toHaveLength(3);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Cat', 'Bird'], ['name', 'bodyTemperature']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Fish'], ['name']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Crocodile'], []);
});
it('should merge fields from the parent case into nested type conditions', () => {
const context = compile(
`
query Animal {
animal {
... on Pet {
name
... on WarmBlooded {
bodyTemperature
}
}
... on WarmBlooded {
bodyTemperature
... on Pet {
name
}
}
}
}
`,
animalSchema
);
const selectionSet = (context.operations['Animal'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Cat', 'Bird', 'Fish', 'Crocodile'], []);
expect(typeCase.variants).toHaveLength(2);
expect(typeCase.variants).toContainSelectionSetMatching(['Cat', 'Bird'], ['name', 'bodyTemperature']);
expect(typeCase.variants).toContainSelectionSetMatching(['Fish'], ['name']);
expect(typeCase.exhaustiveVariants).toHaveLength(3);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Cat', 'Bird'], ['name', 'bodyTemperature']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Fish'], ['name']);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Crocodile'], []);
});
it('should not keep type conditions when all possible objects match', () => {
const context = compile(
`
query Animal {
catOrBird {
... on Pet {
name
... on WarmBlooded {
bodyTemperature
}
}
}
}
`,
animalSchema
);
const selectionSet = (context.operations['Animal'].selectionSet.selections[0] as Field).selectionSet as SelectionSet;
const typeCase = typeCaseForSelectionSet(selectionSet);
expect(typeCase.default).toMatchSelectionSet(['Cat', 'Bird'], ['name', 'bodyTemperature']);
expect(typeCase.variants).toHaveLength(0);
expect(typeCase.exhaustiveVariants).toHaveLength(1);
expect(typeCase.exhaustiveVariants).toContainSelectionSetMatching(['Cat', 'Bird'], ['name', 'bodyTemperature']);
});
});
Example #28
Source File: transform.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('hoist', () => {
const importFn = defaultImportFn;
const schema = buildSchema(/* GraphQL */ `
type Query {
users(limit: Int!, page: Int): UserSearchResult
}
type UserSearchResult {
page: Int!
results: [User!]!
}
type User {
id: ID!
name: String!
}
`);
let cache: InMemoryLRUCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
beforeEach(() => {
cache = new InMemoryLRUCache();
pubsub = new PubSub();
});
it('should hoist field with string pathConfig array', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new HoistFieldTransform({
config: [
{
typeName: 'Query',
pathConfig: ['users', 'results'],
newFieldName: 'users',
},
],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
expect(queryType).toBeDefined();
const fields = queryType.getFields();
expect(fields.users).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should hoist field with mixed pathConfig array', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new HoistFieldTransform({
config: [
{
typeName: 'Query',
pathConfig: [
{
fieldName: 'users',
filterArgs: [],
},
'results',
],
newFieldName: 'users',
},
],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
expect(queryType).toBeDefined();
const fields = queryType.getFields();
expect(fields.users).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should hoist field and filter args with global flag', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new HoistFieldTransform({
config: [
{
typeName: 'Query',
pathConfig: ['users', 'results'],
newFieldName: 'users',
filterArgsInPath: true,
},
],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
expect(queryType).toBeDefined();
const fields = queryType.getFields();
expect(fields.users).toBeDefined();
const args = (fields.users as GraphQLField<any, any>).args;
expect(args.length).toEqual(0);
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should hoist field and filter individual args via pathConfig', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new HoistFieldTransform({
config: [
{
typeName: 'Query',
pathConfig: [
{
fieldName: 'users',
filterArgs: ['limit'],
},
'results',
],
newFieldName: 'users',
},
],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
expect(queryType).toBeDefined();
const fields = queryType.getFields();
expect(fields.users).toBeDefined();
const args = (fields.users as GraphQLField<any, any>).args;
expect(args.length).toEqual(1);
expect(args[0].name).toEqual('page');
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should hoist field and filter individual args via pathConfig independent of global flag', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new HoistFieldTransform({
config: [
{
typeName: 'Query',
pathConfig: [
{
fieldName: 'users',
filterArgs: ['limit'],
},
'results',
],
newFieldName: 'users',
filterArgsInPath: true,
},
],
apiName: '',
cache,
pubsub,
baseDir,
importFn,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
expect(queryType).toBeDefined();
const fields = queryType.getFields();
expect(fields.users).toBeDefined();
const args = (fields.users as GraphQLField<any, any>).args;
expect(args.length).toEqual(1);
expect(args[0].name).toEqual('page');
expect(printSchema(newSchema)).toMatchSnapshot();
});
});
Example #29
Source File: server.ts From client-side-databases with Apache License 2.0 | 4 votes |
export async function run() {
const startData = exampleData;
const state: {
users: WithDeleted<User>[],
messages: WithDeleted<Message>[]
} = {
users: startData.users.map(user => {
const userWithDeleted = Object.assign({ deleted: false }, user);
return userWithDeleted;
}),
messages: []
};
// to faster access all messages for a specific user,
// we index the message by user-id here
const messagesByUser: { [userId: string]: Message[] } = {};
const getMessageArrayOfUser = (userId: string) => {
if (!messagesByUser[userId]) {
messagesByUser[userId] = [];
}
return messagesByUser[userId];
};
const addMessageToState = (message: Message) => {
const messageWithDeleted = Object.assign({ deleted: false }, message);
state.messages.push(messageWithDeleted);
getMessageArrayOfUser(message.sender).push(message);
getMessageArrayOfUser(message.reciever).push(message);
};
const app: Express = express();
// cache options request
app.options('*', (req, res, done) => {
if (req.method === 'OPTIONS') {
log('OPTIONS request');
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, PUT, OPTIONS, DELETE',
'Access-Control-Allow-Credentials': 'false',
'Access-Control-Max-Age': '86400',
'Access-Control-Allow-Headers': 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'
};
res.writeHead(200, headers);
res.end();
} else {
done();
}
});
app.use(cors());
const generatedSchema = graphQLSchemaFromRxSchema({
users: {
schema: RxUsersSchema,
feedKeys: [
'id',
'createdAt'
],
deletedFlag: 'deleted'
},
messages: {
schema: RxMessagesSchema,
feedKeys: [
'id',
'createdAt'
],
deletedFlag: 'deleted'
}
});
const graphQLSchema = generatedSchema.asString;
console.log('GraphQL schema:');
console.log(graphQLSchema);
const schema = buildSchema(graphQLSchema);
const pubsub = new PubSub();
// The root provides a resolver function for each API endpoint
const root = {
info: () => 1,
feedUsers: (args: any) => {
log('## feedUsers()');
console.dir(args);
const users = state.users;
const ret = filterForFeedResult(
users,
args.createdAt,
args.id,
args.limit
);
log('got ' + ret.length + ' users');
return ret;
},
feedMessages: (args: any) => {
log('## feedMessages()');
const ret = filterForFeedResult(
state.messages,
args.createdAt,
args.id,
args.limit
);
console.dir(args);
log('got ' + ret.length + ' messages');
return ret;
},
setMessages: (args: {
messages: Message[]
}) => {
const messages: Message[] = args.messages;
messages.forEach(message => {
log('## addMessage() ' + message.id + ' from ' + message.sender);
// overwrite timestamp
message.createdAt = unixInSeconds();
// log(message);
addMessageToState(message);
pubsub.publish(
'changedMessages',
{
changedMessages: message
}
);
});
},
setUsers: (args: any) => {
log('## registerUser()');
const time = (new Date().getTime() / 1000);
const user = {
id: 'u' + time,
name: args.name,
createdAt: time,
deleted: false
};
state.users.push(user);
pubsub.publish(
'changedUsers',
{
changedUsers: user
}
);
return user;
},
changedUsers: () => pubsub.asyncIterator('changedUsers'),
changedMessages: () => pubsub.asyncIterator('changedMessages')
};
// add start-messages to state
root.setMessages({
messages: startData.messages
});
// server graphql-endpoint
app.use(GRAPHQL_PATH, graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
}));
app.listen(GRAPHQL_PORT, () => {
log('Started graphql-endpoint at http://localhost:' +
GRAPHQL_PORT + GRAPHQL_PATH
);
});
const appSubscription = express();
appSubscription.use(cors);
const serverSubscription = createServer(appSubscription);
serverSubscription.listen(GRAPHQL_SUBSCRIPTION_PORT, () => {
log(
'Started graphql-subscription endpoint at http://localhost:' +
GRAPHQL_SUBSCRIPTION_PORT + GRAPHQL_SUBSCRIPTION_PATH
);
const subServer = new SubscriptionServer(
{
execute,
subscribe,
schema,
rootValue: root
},
{
server: serverSubscription,
path: GRAPHQL_SUBSCRIPTION_PATH,
}
);
return subServer;
});
// comment this in for testing of the subscriptions
/*
setInterval(() => {
const flag = new Date().getTime();
pubsub.publish(
'humanChanged',
{
humanChanged: {
id: 'foobar-' + flag,
name: 'name-' + flag
}
}
);
console.log('published humanChanged ' + flag);
}, 1000);*/
}