graphql#printSchema TypeScript Examples
The following examples show how to use
graphql#printSchema.
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: mercurius.module.ts From nestjs-mercurius with MIT License | 6 votes |
async onModuleInit() {
if (!this.httpAdapterHost) {
return;
}
const httpAdapter = this.httpAdapterHost.httpAdapter;
if (!httpAdapter) {
return;
}
const typeDefs =
(await this.graphqlTypesLoader.mergeTypesByPaths(
this.options.typePaths,
)) || [];
const mergedTypeDefs = extend(typeDefs, this.options.typeDefs);
const mercuriusOptions = (await this.graphqlFactory.mergeOptions({
...this.options,
typeDefs: mergedTypeDefs,
} as any)) as unknown as MercuriusModuleOptions;
if (
this.options.definitions &&
this.options.definitions.path &&
mercuriusOptions.schema instanceof GraphQLSchema
) {
await this.graphqlFactory.generateDefinitions(
printSchema(mercuriusOptions.schema),
this.options as any,
);
}
await this.registerGqlServer(mercuriusOptions);
}
Example #2
Source File: CLI.ts From tql with MIT License | 6 votes |
async function remoteSchema(url: string) {
const { data, errors } = await fetch(url, {
method: "post",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
operationName: "IntrospectionQuery",
query: getIntrospectionQuery(),
}),
}).then((res) => res.json());
if (errors) {
throw new Error("Error fetching remote schema!");
}
return printSchema(buildClientSchema(data));
}
Example #3
Source File: RelayWriter.ts From graphql-ts-client with MIT License | 6 votes |
protected writeCode() {
let relayCode: string;
if (this.indent === STATIC_IDENT) {
relayCode = RELAY_CODE;
} else {
relayCode = RELAY_CODE.replace(STATIC_IDENT, this.indent);
}
this.text(relayCode);
this.text('\nconst typedEnvironment = new TypedEnvironment(`');
this.text(printSchema(this.schema));
this.text('`);');
}
Example #4
Source File: handler.spec.ts From graphql-mesh with MIT License | 6 votes |
describe('thrift', () => {
it('should create a GraphQL Schema from Thrift IDL', async () => {
const thriftHandler = new ThriftHandler({
name: 'Twitter',
config: {
idl: join(__dirname, './fixtures/twitter.thrift'),
hostName: 'localhost',
port: 4444,
path: '/twitter',
serviceName: 'twitter-service',
},
cache: new InMemoryLRUCache(),
pubsub: new PubSub(),
store: new MeshStore('.mesh', new InMemoryStoreStorageAdapter(), { readonly: false, validate: false }),
baseDir: __dirname,
logger: new DefaultLogger('TEST'),
importFn: m => import(m),
});
const source = await thriftHandler.getMeshSource();
expect(printSchema(source.schema)).toMatchSnapshot();
});
});
Example #5
Source File: handler.spec.ts From graphql-mesh with MIT License | 5 votes |
describe.each<[string, string]>([
['Movie', 'movie.proto'],
['Empty', 'empty.proto'],
['Nested', 'nested.proto'],
['Import Nested', 'import-nested.proto'],
['With All Values', 'allvalues.proto'],
['No Package Nested', 'nopackage-nested.proto'],
['With Underscores', 'underscores.proto'],
['Outside', 'outside.proto'],
['Custom Message', 'custom-message.proto'],
['Custom Message2', 'custom-message-2.proto'],
['Comments', 'comments.proto'],
])('Interpreting Protos', (name, file) => {
test(`should load the ${name} proto`, async () => {
const cache = new InMemoryLRUCache();
const pubsub = new PubSub();
const config: YamlConfig.GrpcHandler = {
endpoint: 'localhost',
protoFilePath: {
file: join(__dirname, './fixtures/proto-tests', file),
load: { includeDirs: [join(__dirname, './fixtures/proto-tests')] },
},
};
const store = new MeshStore(name, new InMemoryStoreStorageAdapter(), {
readonly: false,
validate: false,
});
const logger = new DefaultLogger(name);
const handler = new GrpcHandler({
name: Date.now().toString(),
config,
cache,
pubsub,
store,
logger,
importFn: m => import(m),
baseDir: __dirname,
});
const { schema } = await handler.getMeshSource();
expect(schema).toBeInstanceOf(GraphQLSchema);
expect(validateSchema(schema)).toHaveLength(0);
expect(printSchema(schema)).toMatchSnapshot();
});
});
Example #6
Source File: multiple-responses-swagger.test.ts From graphql-mesh with MIT License | 5 votes |
describe('Multiple Responses Swagger', () => {
it('should create correct response types with 204 empty response', async () => {
const schema = await loadGraphQLSchemaFromOpenAPI('test', {
oasFilePath: join(__dirname, 'fixtures', 'multiple-responses-swagger.yml'),
});
expect(printSchema(schema)).toMatchInlineSnapshot(`
"directive @oneOf on INPUT_OBJECT | FIELD_DEFINITION
type Query {
\\"\\"\\"Optional extended description in Markdown.\\"\\"\\"
foo_by_id: foo_by_id_response
}
union foo_by_id_response = Foo | Error
type Foo {
id: String
}
type Error {
message: String
stack: String
}
type Mutation {
\\"\\"\\"Optional extended description in Markdown.\\"\\"\\"
post: post_response
}
union post_response = Void_container | Error
type Void_container {
Void: Void
}
\\"\\"\\"Represents empty values\\"\\"\\"
scalar Void"
`);
});
});
Example #7
Source File: renderSchema.ts From genql with MIT License | 5 votes |
renderSchema = (schema: GraphQLSchema, ctx: RenderContext) => {
ctx.addCodeBlock(printSchema(schema))
}
Example #8
Source File: handler.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('odata', () => {
let pubsub: MeshPubSub;
let cache: KeyValueCache;
let store: MeshStore;
let logger: Logger;
beforeEach(() => {
pubsub = new PubSub();
cache = new InMemoryLRUCache();
store = new MeshStore('odata', new InMemoryStoreStorageAdapter(), {
readonly: false,
validate: false,
});
logger = new DefaultLogger('ODataTest');
resetMocks();
});
it('should create a GraphQL schema from a simple OData endpoint', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
expect(printSchema(source.schema)).toMatchSnapshot();
});
it('should create correct GraphQL schema for functions with entity set paths', async () => {
addMock('http://sample.service.com/$metadata', async () => new Response(BasicMetadata));
const handler = new ODataHandler({
name: 'SampleService',
config: {
baseUrl: 'http://sample.service.com',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
expect(printSchema(source.schema)).toMatchSnapshot();
});
it('should declare arguments for fields created from bound functions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const personType = source.schema.getType('IPerson') as GraphQLInterfaceType;
const getFriendsTripsFunction = personType.getFields().GetFriendsTrips;
expect(getFriendsTripsFunction.args).toHaveLength(2);
const personArg = getFriendsTripsFunction.args.find(arg => arg.name === 'person');
expect(personArg).not.toBeFalsy();
expect(personArg.type.toString()).toBe('PersonInput');
const userNameArg = getFriendsTripsFunction.args.find(arg => arg.name === 'userName');
expect(userNameArg).not.toBeFalsy();
expect(userNameArg.type.toString()).toBe('String!');
});
it('should generate correct HTTP request for requesting an EntitySet', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = 'https://services.odata.org/TripPinRESTierService/People';
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify({ value: [PersonMockData] }));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
People {
UserName
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for requesting a single Entity by ID', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/SOMEID/`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(PersonMockData));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleByUserName(UserName: "SOMEID") {
UserName
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for requesting a complex property', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/Airports/KSFO/?$select=IcaoCode,Location`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
'@odata.type': 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airport',
IcaoCode: Date.now().toString(),
Location: {
'@odata.type': 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.AirportLocation',
Loc: '',
},
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
AirportsByIcaoCode(IcaoCode: "KSFO") {
IcaoCode
Location {
Loc
}
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for query options', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq 'Scott'`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify({ value: [PersonMockData] }));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
People(queryOptions: { filter: "FirstName eq 'Scott'" }) {
UserName
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(decodeURIComponent(sentRequest!.url)).toBe(decodeURIComponent(correctUrl));
});
it('should generate correct HTTP request for $count', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/$count`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(20));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleCount
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for creating an entity', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People`;
const correctMethod = 'POST';
const correctBody = {
UserName: 'lewisblack',
FirstName: 'Lewis',
LastName: 'Black',
Emails: ['[email protected]'],
Gender: 'Male',
FavoriteFeature: 'Feature1',
Features: ['Feature1', 'Feature2'],
AddressInfo: [
{
Address: '187 Suffolk Ln.',
City: {
Name: 'Boise',
CountryRegion: 'United States',
Region: 'ID',
},
},
],
};
let sentRequest: any;
addMock(correctUrl, async request => {
sentRequest = request.clone();
const bodyObj = await request.json();
bodyObj['@odata.type'] = 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person';
return new Response(JSON.stringify(bodyObj));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
variables: {
input: correctBody,
},
document: parse(/* GraphQL */ `
mutation CreatePeople($input: PersonInput) {
createPeople(input: $input) {
UserName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
expect(await sentRequest!.json()).toStrictEqual(correctBody);
});
it('should generate correct HTTP request for deleting an entity', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/SOMEID/`;
const correctMethod = 'DELETE';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify({}));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
mutation {
deletePeopleByUserName(UserName: "SOMEID")
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for updating an entity', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/SOMEID/`;
const correctMethod = 'PATCH';
const correctBody = {
FirstName: 'Mirs',
LastName: 'King',
};
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request.clone();
const returnBody = await request.json();
returnBody['@odata.type'] = 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person';
return new Response(JSON.stringify(returnBody));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
variables: {
UserName: 'SOMEID',
input: correctBody,
},
document: parse(/* GraphQL */ `
mutation UpdatePeople($UserName: String!, $input: PersonUpdateInput!) {
updatePeopleByUserName(UserName: $UserName, input: $input) {
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
expect(await sentRequest!.text()).toBe(JSON.stringify(correctBody));
});
it('should generate correct HTTP request for invoking unbound functions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/GetNearestAirport(lat = 33, lon = -118)?$select=IcaoCode,Name`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
'@odata.type': 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airport',
IcaoCode: Date.now().toString(),
Name: 'Name',
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
GetNearestAirport(lat: 33, lon: -118) {
IcaoCode
Name
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(decodeURIComponent(sentRequest!.url)).toBe(correctUrl);
});
it('should generate correct HTTP request for invoking bound functions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/russellwhyte/Trips/0/Microsoft.OData.Service.Sample.TrippinInMemory.Models.GetInvolvedPeople?$select=UserName`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(`https://services.odata.org/TripPinRESTierService/People/russellwhyte/`, async () => {
return new Response(JSON.stringify(PersonMockData));
});
addMock(
`https://services.odata.org/TripPinRESTierService/People/russellwhyte/Trips?$filter=TripId eq 0&$select=TripId`,
async () => {
return new Response(JSON.stringify(TripMockData));
}
);
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
value: [],
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleByUserName(UserName: "russellwhyte") {
UserName
Trips(queryOptions: { filter: "TripId eq 0" }) {
TripId
GetInvolvedPeople {
UserName
}
}
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for invoking bound functions with arguments', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/russellwhyte/Microsoft.OData.Service.Sample.TrippinInMemory.Models.GetFriendsTrips(userName='ronaldmundy')?$select=TripId,Name`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(`https://services.odata.org/TripPinRESTierService/People/russellwhyte/`, async () => {
return new Response(JSON.stringify(PersonMockData));
});
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
value: [],
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleByUserName(UserName: "russellwhyte") {
UserName
GetFriendsTrips(userName: "ronaldmundy") {
TripId
Name
}
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url.replace(/'/g, '%27')).toBe(correctUrl.replace(/'/g, '%27')); // apostrophe gets percent-encoded
});
it('should generate correct HTTP request for invoking unbound actions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/ResetDataSource`;
const correctMethod = 'POST';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(true));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
mutation {
ResetDataSource
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for invoking bound actions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/russellwhyte/Microsoft.OData.Service.Sample.TrippinInMemory.Models.ShareTrip`;
const correctMethod = 'POST';
const correctBody = {
userName: 'scottketchum',
tripId: 0,
};
let sentRequest: Request;
addMock(`https://services.odata.org/TripPinRESTierService/People/russellwhyte/`, async () => {
return new Response(JSON.stringify(PersonMockData));
});
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(true));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
mutation {
PeopleByUserName(UserName: "russellwhyte") {
ShareTrip(userName: "scottketchum", tripId: 0)
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
expect(await sentRequest!.text()).toBe(JSON.stringify(correctBody));
});
});
Example #9
Source File: json-like-body.test.ts From graphql-mesh with MIT License | 4 votes |
describe('JSON Like Bodies', () => {
it('should treat */* as application/json', async () => {
const openAPISchema = {
openapi: '3.0.1',
info: {
title: 'OpenAPI definition',
version: 'v0',
},
servers: [
{
url: 'https://not-real.com',
description: 'Generated server url',
},
],
paths: {
'/api/v1/countries': {
get: {
tags: ['Attributes'],
operationId: 'retrieveCountries',
responses: {
'200': {
description: 'OK',
content: {
'*/*': {
schema: {
type: 'array',
items: {
$ref: '#/components/schemas/Country',
},
},
},
},
},
},
},
},
'/api/v1/provinces/country/{countryID}': {
get: {
tags: ['Attributes'],
operationId: 'retrieveProvinces',
parameters: [
{
name: 'countryID',
in: 'path',
required: true,
schema: {
type: 'string',
},
},
],
responses: {
'200': {
description: 'OK',
content: {
'*/*': {
schema: {
type: 'array',
items: {
$ref: '#/components/schemas/Province',
},
},
},
},
},
},
},
},
},
components: {
schemas: {
Country: {
type: 'object',
properties: {
code: {
type: 'string',
},
name: {
type: 'string',
},
id: {
type: 'string',
},
},
},
Province: {
type: 'object',
properties: {
country: {
$ref: '#/components/schemas/Country',
},
name: {
type: 'string',
},
id: {
type: 'string',
},
},
},
},
},
};
const graphQLSchema = await createGraphQLSchema(openAPISchema as any);
expect(printSchema(graphQLSchema.schema)).toMatchSnapshot('*/* snapshot');
});
});
Example #10
Source File: index.ts From graphql-mesh with MIT License | 4 votes |
export async function graphqlMesh(
cliParams = DEFAULT_CLI_PARAMS,
args = hideBin(process.argv),
cwdPath = process.cwd()
) {
let baseDir = cwdPath;
let logger: Logger = new DefaultLogger(cliParams.initialLoggerPrefix);
return yargs(args)
.help()
.option('r', {
alias: 'require',
describe: 'Loads specific require.extensions before running the codegen and reading the configuration',
type: 'array' as const,
default: [],
coerce: (externalModules: string[]) =>
Promise.all(
externalModules.map(module => {
const localModulePath = pathModule.resolve(baseDir, module);
const islocalModule = fs.existsSync(localModulePath);
return defaultImportFn(islocalModule ? localModulePath : module);
})
),
})
.option('dir', {
describe: 'Modified the base directory to use for looking for ' + cliParams.configName + ' config file',
type: 'string',
default: baseDir,
coerce: dir => {
if (pathModule.isAbsolute(dir)) {
baseDir = dir;
} else {
baseDir = pathModule.resolve(cwdPath, dir);
}
const tsConfigPath = pathModule.join(baseDir, 'tsconfig.json');
const tsConfigExists = fs.existsSync(tsConfigPath);
tsNodeRegister({
transpileOnly: true,
typeCheck: false,
dir: baseDir,
require: ['graphql-import-node/register'],
compilerOptions: {
module: 'commonjs',
},
});
if (tsConfigExists) {
try {
const tsConfigStr = fs.readFileSync(tsConfigPath, 'utf-8');
const tsConfigStrWithoutComments = stripJSONComments(tsConfigStr);
const tsConfig = JSON.parse(tsConfigStrWithoutComments);
if (tsConfig.compilerOptions?.paths) {
tsConfigPathsRegister({
baseUrl: baseDir,
paths: tsConfig.compilerOptions.paths,
});
}
} catch (e) {
logger.warn(`Unable to read TSConfig file ${tsConfigPath};\n`, e);
}
}
if (fs.existsSync(pathModule.join(baseDir, '.env'))) {
dotEnvRegister({
path: pathModule.join(baseDir, '.env'),
});
}
},
})
.command<{ port: number; prod: boolean; validate: boolean }>(
cliParams.devServerCommand,
'Serves a GraphQL server with GraphQL interface by building artifacts on the fly',
builder => {
builder.option('port', {
type: 'number',
});
},
async args => {
try {
const outputDir = pathModule.join(baseDir, cliParams.artifactsDir);
process.env.NODE_ENV = 'development';
const meshConfig = await findAndParseConfig({
dir: baseDir,
artifactsDir: cliParams.artifactsDir,
configName: cliParams.configName,
additionalPackagePrefixes: cliParams.additionalPackagePrefixes,
});
logger = meshConfig.logger;
const meshInstance$ = getMesh(meshConfig);
meshInstance$
.then(({ schema }) => writeFile(pathModule.join(outputDir, 'schema.graphql'), printSchema(schema)))
.catch(e => {
logger.error(`An error occured while writing the schema file: ${e.stack || e.message}`);
});
meshInstance$
.then(({ schema, rawSources }) =>
generateTsArtifacts(
{
unifiedSchema: schema,
rawSources,
mergerType: meshConfig.merger.name,
documents: meshConfig.documents,
flattenTypes: false,
importedModulesSet: new Set(),
baseDir,
meshConfigCode: `
import { findAndParseConfig } from '@graphql-mesh/cli';
function getMeshOptions() {
console.warn('WARNING: These artifacts are built for development mode. Please run "${
cliParams.commandName
} build" to build production artifacts');
return findAndParseConfig({
dir: baseDir,
artifactsDir: ${JSON.stringify(cliParams.artifactsDir)},
configName: ${JSON.stringify(cliParams.configName)},
additionalPackagePrefixes: ${JSON.stringify(cliParams.additionalPackagePrefixes)},
});
}
`,
logger,
sdkConfig: meshConfig.config.sdk,
fileType: 'ts',
codegenConfig: meshConfig.config.codegen,
},
cliParams
)
)
.catch(e => {
logger.error(`An error occurred while building the artifacts: ${e.stack || e.message}`);
});
const serveMeshOptions: ServeMeshOptions = {
baseDir,
argsPort: args.port,
getBuiltMesh: () => meshInstance$,
logger: meshConfig.logger.child('Server'),
rawServeConfig: meshConfig.config.serve,
};
if (meshConfig.config.serve?.customServerHandler) {
const customServerHandler = await loadFromModuleExportExpression<any>(
meshConfig.config.serve.customServerHandler,
{
defaultExportName: 'default',
cwd: baseDir,
importFn: defaultImportFn,
}
);
await customServerHandler(serveMeshOptions);
} else {
await serveMesh(serveMeshOptions, cliParams);
}
} catch (e) {
handleFatalError(e, logger);
}
}
)
.command<{ port: number; prod: boolean; validate: boolean }>(
cliParams.prodServerCommand,
'Serves a GraphQL server with GraphQL interface based on your generated artifacts',
builder => {
builder.option('port', {
type: 'number',
});
},
async args => {
try {
const builtMeshArtifactsPath = pathModule.join(baseDir, cliParams.artifactsDir);
if (!(await pathExists(builtMeshArtifactsPath))) {
throw new Error(
`Seems like you haven't build the artifacts yet to start production server! You need to build artifacts first with "${cliParams.commandName} build" command!`
);
}
process.env.NODE_ENV = 'production';
const mainModule = pathModule.join(builtMeshArtifactsPath, 'index');
const builtMeshArtifacts = await defaultImportFn(mainModule);
const getMeshOptions: GetMeshOptions = await builtMeshArtifacts.getMeshOptions();
logger = getMeshOptions.logger;
const rawServeConfig: YamlConfig.Config['serve'] = builtMeshArtifacts.rawServeConfig;
const serveMeshOptions: ServeMeshOptions = {
baseDir,
argsPort: args.port,
getBuiltMesh: () => getMesh(getMeshOptions),
logger: getMeshOptions.logger.child('Server'),
rawServeConfig,
};
if (rawServeConfig?.customServerHandler) {
const customServerHandler = await loadFromModuleExportExpression<any>(rawServeConfig.customServerHandler, {
defaultExportName: 'default',
cwd: baseDir,
importFn: defaultImportFn,
});
await customServerHandler(serveMeshOptions);
} else {
await serveMesh(serveMeshOptions, cliParams);
}
} catch (e) {
handleFatalError(e, logger);
}
}
)
.command(
cliParams.validateCommand,
'Validates artifacts',
builder => {},
async args => {
let destroy: VoidFunction;
try {
if (!(await pathExists(pathModule.join(baseDir, cliParams.artifactsDir)))) {
throw new Error(
`You cannot validate artifacts now because you don't have built artifacts yet! You need to build artifacts first with "${cliParams.commandName} build" command!`
);
}
const store = new MeshStore(
cliParams.artifactsDir,
new FsStoreStorageAdapter({
cwd: baseDir,
importFn: defaultImportFn,
fileType: 'ts',
}),
{
readonly: false,
validate: true,
}
);
logger.info(`Reading the configuration`);
const meshConfig = await findAndParseConfig({
dir: baseDir,
store,
importFn: defaultImportFn,
ignoreAdditionalResolvers: true,
artifactsDir: cliParams.artifactsDir,
configName: cliParams.configName,
additionalPackagePrefixes: cliParams.additionalPackagePrefixes,
});
logger = meshConfig.logger;
logger.info(`Generating the unified schema`);
const mesh = await getMesh(meshConfig);
logger.info(`Artifacts have been validated successfully`);
destroy = mesh?.destroy;
} catch (e) {
handleFatalError(e, logger);
}
if (destroy) {
destroy();
}
}
)
.command<{ fileType: 'json' | 'ts' | 'js' }>(
cliParams.buildArtifactsCommand,
'Builds artifacts',
builder => {
builder.option('fileType', {
type: 'string',
choices: ['json', 'ts', 'js'],
default: 'ts',
});
},
async args => {
try {
const outputDir = pathModule.join(baseDir, cliParams.artifactsDir);
logger.info('Cleaning existing artifacts');
await rmdirs(outputDir);
const importedModulesSet = new Set<string>();
const importPromises: Promise<any>[] = [];
const importFn = (moduleId: string, noCache: boolean) => {
const importPromise = defaultImportFn(moduleId)
.catch(e => {
if (e.message.includes('getter')) {
return e;
} else {
throw e;
}
})
.then(m => {
if (!noCache) {
importedModulesSet.add(moduleId);
}
return m;
});
importPromises.push(importPromise.catch(() => {}));
return importPromise;
};
await Promise.all(importPromises);
const store = new MeshStore(
cliParams.artifactsDir,
new FsStoreStorageAdapter({
cwd: baseDir,
importFn,
fileType: args.fileType,
}),
{
readonly: false,
validate: false,
}
);
logger.info(`Reading the configuration`);
const meshConfig = await findAndParseConfig({
dir: baseDir,
store,
importFn,
ignoreAdditionalResolvers: true,
artifactsDir: cliParams.artifactsDir,
configName: cliParams.configName,
additionalPackagePrefixes: cliParams.additionalPackagePrefixes,
generateCode: true,
});
logger = meshConfig.logger;
logger.info(`Generating the unified schema`);
const { schema, destroy, rawSources } = await getMesh(meshConfig);
await writeFile(pathModule.join(outputDir, 'schema.graphql'), printSchema(schema));
logger.info(`Generating artifacts`);
await generateTsArtifacts(
{
unifiedSchema: schema,
rawSources,
mergerType: meshConfig.merger.name,
documents: meshConfig.documents,
flattenTypes: false,
importedModulesSet,
baseDir,
meshConfigCode: meshConfig.code,
logger,
sdkConfig: meshConfig.config.sdk,
fileType: args.fileType,
codegenConfig: meshConfig.config.codegen,
},
cliParams
);
logger.info(`Cleanup`);
destroy();
logger.info('Done! => ' + outputDir);
} catch (e) {
handleFatalError(e, logger);
}
}
)
.command<{ source: string }>(
cliParams.sourceServerCommand + ' <source>',
'Serves specific source in development mode',
builder => {
builder.positional('source', {
type: 'string',
requiresArg: true,
});
},
async args => {
process.env.NODE_ENV = 'development';
const meshConfig = await findAndParseConfig({
dir: baseDir,
artifactsDir: cliParams.artifactsDir,
configName: cliParams.configName,
additionalPackagePrefixes: cliParams.additionalPackagePrefixes,
});
logger = meshConfig.logger;
const sourceIndex = meshConfig.sources.findIndex(rawSource => rawSource.name === args.source);
if (sourceIndex === -1) {
throw new Error(`Source ${args.source} not found`);
}
const meshInstance$ = getMesh({
...meshConfig,
additionalTypeDefs: undefined,
additionalResolvers: [],
transforms: [],
sources: [meshConfig.sources[sourceIndex]],
});
const serveMeshOptions: ServeMeshOptions = {
baseDir,
argsPort: 4000 + sourceIndex + 1,
getBuiltMesh: () => meshInstance$,
logger: meshConfig.logger.child('Server'),
rawServeConfig: meshConfig.config.serve,
playgroundTitle: `${args.source} GraphiQL`,
};
if (meshConfig.config.serve?.customServerHandler) {
const customServerHandler = await loadFromModuleExportExpression<any>(
meshConfig.config.serve.customServerHandler,
{
defaultExportName: 'default',
cwd: baseDir,
importFn: defaultImportFn,
}
);
await customServerHandler(serveMeshOptions);
} else {
await serveMesh(serveMeshOptions, cliParams);
}
}
).argv;
}
Example #11
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 #12
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 #13
Source File: naming-convention.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('namingConvention', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
user: user!
userById(userId: ID!): user!
}
type user {
Id: ID!
Type: userType
}
enum userType {
admin
moderator
newbie
}
`);
let cache: InMemoryLRUCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
const importFn: ImportFn = m => import(m);
beforeEach(() => {
cache = new InMemoryLRUCache();
pubsub = new PubSub();
});
it('should change the name of a types, enums, fields and fieldArguments', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new NamingConventionTransform({
apiName: '',
importFn,
config: {
typeNames: 'pascalCase',
enumValues: 'upperCase',
fieldNames: 'camelCase',
fieldArgumentNames: 'snakeCase',
},
cache,
pubsub,
baseDir,
}),
],
});
expect(newSchema.getType('user')).toBeUndefined();
const userObjectType = newSchema.getType('User') as GraphQLObjectType;
expect(userObjectType).toBeDefined();
const userObjectTypeFields = userObjectType.getFields();
expect(userObjectTypeFields.Id).toBeUndefined();
expect(userObjectTypeFields.id).toBeDefined();
expect(newSchema.getType('userType')).toBeUndefined();
const userTypeEnumType = newSchema.getType('UserType') as GraphQLEnumType;
expect(userTypeEnumType).toBeDefined();
expect(userTypeEnumType.getValue('Admin')).toBeUndefined();
const adminValue = userTypeEnumType.getValue('ADMIN');
expect(adminValue).toBeDefined();
// expect(adminValue.value).toBe('admin');
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should execute the transformed schema properly', async () => {
let schema = buildSchema(/* GraphQL */ `
type Query {
user(input: UserSearchInput): User
userById(userId: ID!): User
}
type User {
id: ID
first_name: String
last_name: String
}
input UserSearchInput {
id: ID
first_name: String
last_name: String
}
`);
let userInput;
schema = addResolversToSchema({
schema,
resolvers: {
Query: {
user: (root, args, context, info) => {
userInput = args?.input;
return userInput;
},
userById: (root, args, context, info) => {
return { id: args.userId, first_name: 'John', last_name: 'Doe' };
},
},
},
});
schema = wrapSchema({
schema,
transforms: [
new NamingConventionTransform({
apiName: '',
importFn,
cache,
pubsub,
config: {
fieldNames: 'camelCase',
fieldArgumentNames: 'snakeCase',
},
baseDir,
}),
],
});
const result = await execute({
schema,
document: parse(/* GraphQL */ `
{
user(input: { id: "0", firstName: "John", lastName: "Doe" }) {
id
firstName
lastName
}
}
`),
});
// Pass non-transformed input to the real schema
expect(userInput).toEqual({
id: '0',
first_name: 'John',
last_name: 'Doe',
});
// Pass transformed output to the client
expect(result?.data?.user).toEqual({
id: '0',
firstName: 'John',
lastName: 'Doe',
});
const result2 = await execute({
schema,
document: parse(/* GraphQL */ `
{
userById(user_id: "1") {
id
firstName
lastName
}
}
`),
});
// Pass transformed output to the client
expect(result2.data?.userById).toEqual({
id: '1',
firstName: 'John',
lastName: 'Doe',
});
});
it('should be skipped if the result gonna be empty string', async () => {
let schema = buildSchema(/* GraphQL */ `
type Query {
_: String!
}
`);
schema = addResolversToSchema({
schema,
resolvers: {
Query: {
_: (root, args, context, info) => {
return 'test';
},
},
},
});
schema = wrapSchema({
schema,
transforms: [
new NamingConventionTransform({
apiName: '',
importFn,
cache,
pubsub,
config: {
fieldNames: 'camelCase',
},
baseDir,
}),
],
});
const { data } = await execute({
schema,
document: parse(/* GraphQL */ `
{
_
}
`),
});
expect(data?._).toEqual('test');
});
it('should skip fields of Federation spec', async () => {
const typeDefs = /* GraphQL */ `
type Query {
_service: String!
_entities: [String!]!
}`.trim();
const schema = wrapSchema({
schema: buildSchema(typeDefs),
transforms: [
new NamingConventionTransform({
apiName: '',
importFn,
cache,
pubsub,
config: {
fieldNames: 'snakeCase',
},
baseDir,
}),
],
});
expect(printSchema(schema)).toBe(typeDefs);
});
});
Example #14
Source File: barePrefix.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('barePrefix', () => {
let schema: GraphQLSchema;
let cache: InMemoryLRUCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
beforeEach(() => {
schema = buildSchema(/* GraphQL */ `
type Query {
user: User!
posts: [Post!]!
}
type User {
id: ID!
}
type Post {
id: ID!
title: String!
}
`);
cache = new InMemoryLRUCache();
pubsub = new PubSub();
});
it('should prefix all schema types when prefix is specified explicitly', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('User')).toBeUndefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should not modify root types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_Query')).toBeUndefined();
});
it('should not modify default scalar types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
const postFields = (newSchema.getType('T_Post') as GraphQLObjectType).getFields();
expect(postFields.id.type.toString()).toBe('ID!');
expect(postFields.title.type.toString()).toBe('String!');
});
it('should use apiName when its available', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
apiName: 'MyApi',
config: { mode: 'bare' },
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('User')).toBeUndefined();
expect(newSchema.getType('MyApi_User')).toBeDefined();
});
it('should allow to ignore types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
ignore: ['User'],
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('User')).toBeDefined();
});
it('should modify fields', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
includeRootOperations: true,
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect((newSchema.getType('Query') as GraphQLObjectType).getFields()).toHaveProperty('T_user');
});
it('should allow to ignore all fields in Type', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
includeRootOperations: true,
ignore: ['Query'],
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
const queryFields = (newSchema.getType('Query') as GraphQLObjectType).getFields();
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect(newSchema.getType('User')).toBeUndefined();
expect(queryFields).not.toHaveProperty('T_user');
expect(queryFields).toHaveProperty('user');
expect(queryFields).not.toHaveProperty('T_posts');
expect(queryFields).toHaveProperty('posts');
});
it('should allow to ignore specific fields', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
mode: 'bare',
value: 'T_',
includeRootOperations: true,
ignore: ['Query.user'],
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
const queryFields = (newSchema.getType('Query') as GraphQLObjectType).getFields();
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect(newSchema.getType('User')).toBeUndefined();
expect(queryFields).not.toHaveProperty('T_user');
expect(queryFields).toHaveProperty('user');
expect(queryFields).toHaveProperty('T_posts');
expect(queryFields).not.toHaveProperty('posts');
});
});
Example #15
Source File: wrapPrefix.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('wrapPrefix', () => {
let schema: GraphQLSchema;
let cache: InMemoryLRUCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
beforeEach(() => {
schema = buildSchema(/* GraphQL */ `
type Query {
user: User!
posts: [Post!]!
}
type User {
id: ID!
}
type Post {
id: ID!
title: String!
}
`);
cache = new InMemoryLRUCache();
pubsub = new PubSub();
});
it('should prefix all schema types when prefix is specified explicitly', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('User')).toBeUndefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect((newSchema.getType('Query') as GraphQLObjectType).getFields()).not.toHaveProperty('T_user');
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should not modify root types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_Query')).toBeUndefined();
});
it('should not modify default scalar types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
const postFields = (newSchema.getType('T_Post') as GraphQLObjectType).getFields();
expect(postFields.id.type.toString()).toBe('ID!');
expect(postFields.title.type.toString()).toBe('String!');
});
it('should use apiName when its available', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {},
apiName: 'MyApi',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('User')).toBeUndefined();
expect(newSchema.getType('MyApi_User')).toBeDefined();
});
it('should allow to ignore types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
ignore: ['User'],
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('User')).toBeDefined();
});
it('should modify fields', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
includeRootOperations: true,
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect((newSchema.getType('Query') as GraphQLObjectType).getFields()).toHaveProperty('T_user');
});
it('should allow to ignore all fields in Type', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
includeRootOperations: true,
ignore: ['Query'],
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
const queryFields = (newSchema.getType('Query') as GraphQLObjectType).getFields();
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect(newSchema.getType('User')).toBeUndefined();
expect(queryFields).not.toHaveProperty('T_user');
expect(queryFields).toHaveProperty('user');
expect(queryFields).not.toHaveProperty('T_posts');
expect(queryFields).toHaveProperty('posts');
});
it('should allow to ignore specific fields', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
includeRootOperations: true,
ignore: ['Query.user'],
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
const queryFields = (newSchema.getType('Query') as GraphQLObjectType).getFields();
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeDefined();
expect(newSchema.getType('User')).toBeUndefined();
expect(queryFields).not.toHaveProperty('T_user');
expect(queryFields).toHaveProperty('user');
expect(queryFields).toHaveProperty('T_posts');
expect(queryFields).not.toHaveProperty('posts');
});
it('should allow to ignore types', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new PrefixTransform({
config: {
value: 'T_',
includeRootOperations: true,
includeTypes: false,
},
apiName: '',
baseDir,
cache,
pubsub,
importFn: m => import(m),
}),
],
});
expect(newSchema.getType('Query')).toBeDefined();
expect(newSchema.getType('T_User')).toBeUndefined();
expect(newSchema.getType('User')).toBeDefined();
});
});
Example #16
Source File: wrapRename.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('rename', () => {
const schema = makeExecutableSchema({
typeDefs: /* GraphQL */ `
type Query {
my_user: MyUser!
my_book: MyBook!
profile(profile_id: ID!, role: String, some_argument: String, another_argument: Int): Profile
}
type MyUser {
id: ID!
}
type Profile {
id: ID!
}
type MyBook {
id: ID!
}
`,
resolvers: {
Query: { my_user: () => ({ id: 'userId' }), profile: (_, args) => ({ id: `profile_${args.profile_id}` }) },
},
});
let cache: InMemoryLRUCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
beforeEach(() => {
cache = new InMemoryLRUCache();
pubsub = new PubSub();
});
it('should change the name of a type', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
renames: [
{
from: {
type: 'MyUser',
},
to: {
type: 'User',
},
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
expect(newSchema.getType('MyUser')).toBeUndefined();
expect(newSchema.getType('User')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should change the name of a field', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'Query',
field: 'my_user',
},
to: {
type: 'Query',
field: 'user',
},
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.my_user).toBeUndefined();
expect(fieldMap.user).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should resolve correctly renamed field', async () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'MyUser',
field: 'id',
},
to: {
type: 'MyUser',
field: 'userId',
},
},
],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn: m => import(m),
}),
],
});
const result = await graphql({
schema: newSchema,
source: /* GraphQL */ `
{
my_user {
userId
}
}
`,
});
expect(result.data).toMatchObject({ my_user: { userId: 'userId' } });
});
it('should change the name of multiple type names', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'My(.*)',
},
to: {
type: '$1',
},
useRegExpForTypes: true,
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
expect(newSchema.getType('MyUser')).toBeUndefined();
expect(newSchema.getType('User')).toBeDefined();
expect(newSchema.getType('MyBook')).toBeUndefined();
expect(newSchema.getType('Book')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should change the name of multiple fields', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
renames: [
{
from: {
type: 'Query',
field: 'my_(.*)',
},
to: {
type: 'Query',
field: '$1',
},
useRegExpForFields: true,
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.my_user).toBeUndefined();
expect(fieldMap.user).toBeDefined();
expect(fieldMap.my_book).toBeUndefined();
expect(fieldMap.book).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should replace the first occurrence of a substring in a field', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'Query',
field: 'o(.*)',
},
to: {
type: 'Query',
field: '$1',
},
useRegExpForFields: true,
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.my_book).toBeUndefined();
expect(fieldMap.my_bok).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should replace all occurrences of a substring in a type', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
api_user_v1_api: ApiUserV1Api!
}
type ApiUserV1Api {
id: ID!
}
`);
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
renames: [
{
from: {
type: 'Api(.*?)',
},
to: {
type: '$1',
},
useRegExpForTypes: true,
regExpFlags: 'g',
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
expect(newSchema.getType('ApiUserV1Api')).toBeUndefined();
expect(newSchema.getType('UserV1')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should replace all occurrences of multiple substrings in a type', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
api_user_v1_api: ApiUserV1Api!
}
type ApiUserV1Api {
id: ID!
}
`);
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
renames: [
{
from: {
type: 'Api|V1(.*?)',
},
to: {
type: '$1',
},
useRegExpForTypes: true,
regExpFlags: 'g',
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
expect(newSchema.getType('ApiUserV1Api')).toBeUndefined();
expect(newSchema.getType('User')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should replace all occurrences of a substring in a field', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
api_user_v1_api: ApiUserV1Api!
}
type ApiUserV1Api {
id: ID!
}
`);
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
renames: [
{
from: {
type: 'Query',
field: 'api_|_api(.*?)',
},
to: {
type: 'Query',
field: '$1',
},
useRegExpForFields: true,
regExpFlags: 'g',
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.api_user_v1_api).toBeUndefined();
expect(fieldMap.user_v1).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should replace all occurrences of multiple substrings in a field', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
api_user_v1_api: ApiUserV1Api!
}
type ApiUserV1Api {
id: ID!
}
`);
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
renames: [
{
from: {
type: 'Query',
field: 'api_|_api|v1_|_v1(.*?)',
},
to: {
type: 'Query',
field: '$1',
},
useRegExpForFields: true,
regExpFlags: 'g',
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.api_user_v1_api).toBeUndefined();
expect(fieldMap.user).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should only affect specified type', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'Query',
field: 'o(.*)',
},
to: {
type: 'Query',
field: '$1',
},
useRegExpForFields: true,
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.my_book).toBeUndefined();
expect(fieldMap.my_bok).toBeDefined();
const myUserType = newSchema.getType('MyUser') as GraphQLObjectType;
const myUserFields = myUserType.getFields();
expect(myUserFields.id).toBeDefined();
const myBookType = newSchema.getType('MyBook') as GraphQLObjectType;
const myBookFields = myBookType.getFields();
expect(myBookFields.id).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should only affect specified field argument', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'Query',
field: 'profile',
argument: 'profile_id',
},
to: {
type: 'Query',
field: 'profile',
argument: 'profileId',
},
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
// TODO: uncomment following line once #3778 is fixed
// expect(fieldMap.profile.args.find(a => a.name === 'role')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'profile_id')).toBeUndefined();
expect(fieldMap.profile.args.find(a => a.name === 'profileId')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'another_argument')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'some_argument')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should only affect specified field match argument', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'Query',
field: 'profile',
argument: '(profile)_(id)',
},
to: {
type: 'Query',
field: 'profile',
argument: '$1Id',
},
useRegExpForArguments: true,
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
// TODO: uncomment following line once #3778 is fixed
// expect(fieldMap.profile.args.find(a => a.name === 'role')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'profile_id')).toBeUndefined();
expect(fieldMap.profile.args.find(a => a.name === 'profileId')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'another_argument')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'some_argument')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should only affect specified match type and match field argument', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
type: '(.uer.)',
field: '(.rofil.)',
argument: 'profile_id',
},
to: {
type: '$1',
field: '$1',
argument: 'profileId',
},
useRegExpForTypes: true,
useRegExpForFields: true,
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
// TODO: uncomment following line once #3778 is fixed
// expect(fieldMap.profile.args.find(a => a.name === 'role')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'profile_id')).toBeUndefined();
expect(fieldMap.profile.args.find(a => a.name === 'profileId')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'another_argument')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'some_argument')).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
it('should resolve correctly field with renamed argument', async () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
config: {
mode: 'wrap',
renames: [
{
from: {
type: 'Query',
field: 'profile',
argument: 'profile_id',
},
to: {
type: 'Query',
field: 'profile',
argument: 'profileId',
},
},
],
},
apiName: '',
cache,
pubsub,
baseDir,
importFn: m => import(m),
}),
],
});
const result = await graphql({
schema: newSchema,
source: /* GraphQL */ `
{
profile(profileId: "abc123") {
id
}
}
`,
});
expect(result.data).toMatchObject({ profile: { id: 'profile_abc123' } });
});
it('should only affect field argument only if type and field are specified', () => {
const newSchema = wrapSchema({
schema,
transforms: [
new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'wrap',
renames: [
{
from: {
argument: 'profile_id',
},
to: {
argument: 'profileId',
},
},
],
},
cache,
pubsub,
baseDir,
}),
],
});
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const fieldMap = queryType.getFields();
expect(fieldMap.profile.args.find(a => a.name === 'role')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'profile_id')).toBeDefined();
expect(fieldMap.profile.args.find(a => a.name === 'profileId')).toBeUndefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
// TODO
it.skip('should move a root field from a root type to another', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
foo: String
}
type Mutation {
bar: String
}
`);
const transform = new RenameTransform({
apiName: '',
importFn: m => import(m),
config: {
mode: 'bare',
renames: [
{
from: {
type: 'Mutation',
field: 'bar',
},
to: {
type: 'Query',
field: 'bar',
},
},
],
},
cache,
pubsub,
baseDir,
});
const newSchema = transform.transformSchema(schema, {} as any);
const queryType = newSchema.getType('Query') as GraphQLObjectType;
const queryFieldMap = queryType.getFields();
expect(queryFieldMap.bar).toBeDefined();
expect(printSchema(newSchema)).toMatchSnapshot();
});
});
Example #17
Source File: replace-field.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('replace-field', () => {
const mockQueryBooks = jest.fn().mockImplementation(() => ({ books: [{ title: 'abc' }, { title: 'def' }] }));
const mockBooksApiResponseBooks = jest.fn().mockImplementation(() => [{ title: 'ghi' }, { title: 'lmn' }]);
const schemaDefs = /* GraphQL */ `
type Query {
books: BooksApiResponse
}
type BooksApiResponse {
"""
Retrieve a list of Books
"""
books(maxResults: Int, orderBy: String): [Book]
}
type Book {
title: String!
author: Author!
code: String
}
type Author {
name: String!
age: Int!
}
`;
let cache: InMemoryLRUCache;
let pubsub: MeshPubSub;
const baseDir: string = undefined;
beforeEach(() => {
jest.clearAllMocks();
});
afterEach(() => {
cache = new InMemoryLRUCache();
pubsub = new PubSub();
});
it('should replace correctly field Type only', async () => {
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('BooksApiResponse')).toBeUndefined();
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().books.type.toString()).toBe('[Book]');
expect(printSchema(transformedSchema)).toMatchSnapshot();
});
it('should replace correctly field Type with additional type definitions', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', author: { age: 50 } },
{ title: 'def', author: {} },
],
});
const transform = new ReplaceFieldTransform({
config: {
typeDefs: /* GraphQL */ `
type NewAuthor {
age: String
}
`,
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
scope: 'hoistValue',
},
{
from: {
type: 'Author',
field: 'age',
},
to: {
type: 'NewAuthor',
field: 'age',
},
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect((transformedSchema.getType('Author') as GraphQLObjectType).getFields().age.type.toString()).toBe('String');
expect(printSchema(transformedSchema)).toMatchSnapshot();
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
title
author {
age
}
}
}
`),
});
expect(mockQueryBooks).toHaveBeenCalledTimes(1);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
expect(result.data.books).toEqual([
{ title: 'abc', author: { age: '50' } },
{ title: 'def', author: { age: null } },
]);
});
it('should replace correctly field with hoistValue and resolver function', async () => {
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
scope: 'hoistValue',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('BooksApiResponse')).toBeUndefined();
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().books.type.toString()).toBe('[Book]');
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
title
}
}
`),
});
expect(mockQueryBooks).toHaveBeenCalledTimes(1);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
expect(result.data.books).toEqual([{ title: 'abc' }, { title: 'def' }]);
});
it('should replace correctly field with hoistValue and default-field-resolver', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', author: { name: 'abra' } },
{ title: 'def', author: { name: 'cadabra' } },
],
});
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Book',
field: 'author',
},
to: {
type: 'Author',
field: 'name',
},
scope: 'hoistValue',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('Author')).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().author.type.toString()).toBe('String!');
const result: any = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
books {
title
author
}
}
}
`),
});
expect(result.data.books.books).toEqual([
{ title: 'abc', author: 'abra' },
{ title: 'def', author: 'cadabra' },
]);
});
it('should replace correctly mtultiple fields with hoistValue and defined resolver function as well as default-field-resolver', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', author: { name: 'abra' } },
{ title: 'def', author: { name: 'cadabra' } },
],
});
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
scope: 'hoistValue',
},
{
from: {
type: 'Book',
field: 'author',
},
to: {
type: 'Author',
field: 'name',
},
scope: 'hoistValue',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('BooksApiResponse')).toBeUndefined();
expect(transformedSchema.getType('Author')).toBeUndefined();
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().books.type.toString()).toBe('[Book]');
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().author.type.toString()).toBe('String!');
expect(printSchema(transformedSchema)).toMatchSnapshot();
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
title
author
}
}
`),
});
expect(mockQueryBooks).toHaveBeenCalledTimes(1);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
expect(result.data.books).toEqual([
{ title: 'abc', author: 'abra' },
{ title: 'def', author: 'cadabra' },
]);
});
it('should replace correctly field with composer wrapping resolver function', async () => {
mockQueryBooks.mockReturnValueOnce({
availableBooks: [{ title: 'abc' }, { title: 'def' }],
});
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
composer: join(__dirname, './fixtures/composer.js#books'),
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
title
isAvailable
}
}
`),
});
expect(mockQueryBooks).toHaveBeenCalledTimes(1);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
expect(result.data.books).toEqual([{ title: 'abc' }, { title: 'def' }]);
});
it('should replace correctly field with composer wrapping default-field-resolver', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', code: 'def' },
{ title: 'ghi', code: 'lmn' },
],
});
const transform = new ReplaceFieldTransform({
config: {
typeDefs: /* GraphQL */ `
type NewBook {
code: String!
}
`,
replacements: [
{
from: {
type: 'Book',
field: 'code',
},
to: {
type: 'NewBook',
field: 'code',
},
composer: join(__dirname, './fixtures/composer.js#code'),
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().code.type.toString()).toBe('String!');
const result: any = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
books {
title
code
}
}
}
`),
});
expect(result.data.books.books).toEqual([
{ title: 'abc', code: 'store001_def' },
{ title: 'ghi', code: 'store001_lmn' },
]);
});
it('should replace correctly renamed field with Type only', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', author: { name: 'abra' } },
{ title: 'def', author: { name: 'cadabra' } },
],
});
const transform = new ReplaceFieldTransform({
config: {
typeDefs: /* GraphQL */ `
type NewAuthor {
name: String
}
`,
replacements: [
{
from: {
type: 'Author',
field: 'name',
},
to: {
type: 'NewAuthor',
field: 'name',
},
name: 'fullName',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect((transformedSchema.getType('Author') as GraphQLObjectType).getFields().name).toBeUndefined();
expect((transformedSchema.getType('Author') as GraphQLObjectType).getFields().fullName.type.toString()).toBe(
'String'
);
const result: any = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
books {
title
author {
fullName
}
}
}
}
`),
});
expect(result.data.books.books).toEqual([
{ title: 'abc', author: { fullName: 'abra' } },
{ title: 'def', author: { fullName: 'cadabra' } },
]);
});
it('should replace correctly renamed field with hoistValue and resolver function', async () => {
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
scope: 'hoistValue',
name: 'ourBooks',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('BooksApiResponse')).toBeUndefined();
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().books).toBeUndefined();
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().ourBooks.type.toString()).toBe(
'[Book]'
);
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
ourBooks {
title
}
}
`),
});
expect(mockQueryBooks).toHaveBeenCalledTimes(1);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
expect(result.data.ourBooks).toEqual([{ title: 'abc' }, { title: 'def' }]);
});
it('should replace correctly renamed field with hoistValue and default-field-resolver', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', author: { name: 'abra' } },
{ title: 'def', author: { name: 'cadabra' } },
],
});
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Book',
field: 'author',
},
to: {
type: 'Author',
field: 'name',
},
name: 'authorName',
scope: 'hoistValue',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('Author')).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().author).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().authorName.type.toString()).toBe(
'String!'
);
const result: any = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
books {
title
authorName
}
}
}
`),
});
expect(result.data.books.books).toEqual([
{ title: 'abc', authorName: 'abra' },
{ title: 'def', authorName: 'cadabra' },
]);
});
it('should replace correctly renamed field with composer wrapping resolver function', async () => {
mockQueryBooks.mockReturnValueOnce({
availableBooks: [{ title: 'abc' }, { title: 'def' }],
});
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
name: 'ourBooks',
composer: join(__dirname, './fixtures/composer.js#books'),
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().books).toBeUndefined();
expect((transformedSchema.getType('Query') as GraphQLObjectType).getFields().ourBooks.type.toString()).toBe(
'[Book]'
);
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
ourBooks {
title
isAvailable
}
}
`),
});
expect(mockQueryBooks).toHaveBeenCalledTimes(1);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
expect(result.data.ourBooks).toEqual([{ title: 'abc' }, { title: 'def' }]);
});
it('should replace correctly renamed field with composer wrapping default-field-resolver', async () => {
mockQueryBooks.mockReturnValueOnce({
books: [
{ title: 'abc', code: undefined },
{ title: 'def', code: 'ghi' },
],
});
const transform = new ReplaceFieldTransform({
config: {
typeDefs: /* GraphQL */ `
type NewBook {
isAvailable: Boolean
}
`,
replacements: [
{
from: {
type: 'Book',
field: 'code',
},
to: {
type: 'NewBook',
field: 'isAvailable',
},
name: 'isAvailable',
composer: join(__dirname, './fixtures/composer.js#isAvailable'),
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: {
books: mockQueryBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().code).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().isAvailable.type.toString()).toBe(
'Boolean'
);
const result: any = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
books {
title
isAvailable
}
}
}
`),
});
expect(result.data.books.books).toEqual([
{ title: 'abc', isAvailable: false },
{ title: 'def', isAvailable: true },
]);
});
it('should replace correctly whole field config', async () => {
const transform = new ReplaceFieldTransform({
config: {
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
scope: 'config',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
resolvers: {
Query: { books: mockQueryBooks },
BooksApiResponse: {
books: mockBooksApiResponseBooks,
},
},
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
const queryBooks = (transformedSchema.getType('Query') as GraphQLObjectType).getFields().books;
expect(printSchema(transformedSchema)).toMatchSnapshot();
expect(transformedSchema.getType('BooksApiResponse')).toBeUndefined();
expect(queryBooks.type.toString()).toBe('[Book]');
expect(queryBooks.description).toBe('Retrieve a list of Books');
expect(queryBooks.args).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: 'maxResults' }),
expect.objectContaining({ name: 'orderBy' }),
])
);
expect(mockBooksApiResponseBooks).not.toHaveBeenCalled();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
await queryBooks.resolve();
expect(mockBooksApiResponseBooks).toHaveBeenCalledTimes(1);
const result = await execute({
schema: transformedSchema,
document: parse(/* GraphQL */ `
{
books {
title
}
}
`),
});
expect(mockQueryBooks).not.toHaveBeenCalled();
expect(mockBooksApiResponseBooks).toHaveBeenCalledTimes(2);
expect(result.data.books).toEqual([{ title: 'ghi' }, { title: 'lmn' }]);
});
it('applies multiple replaces to obtain a cleaner schema', () => {
const transform = new ReplaceFieldTransform({
config: {
typeDefs: /* GraphQL */ `
type NewBook {
isAvailable: Boolean
}
`,
replacements: [
{
from: {
type: 'Query',
field: 'books',
},
to: {
type: 'BooksApiResponse',
field: 'books',
},
},
{
from: {
type: 'Book',
field: 'code',
},
to: {
type: 'NewBook',
field: 'isAvailable',
},
name: 'isAvailable',
composer: join(__dirname, './fixtures/composer.js#isAvailable'),
},
{
from: {
type: 'Book',
field: 'author',
},
to: {
type: 'Author',
field: 'name',
},
scope: 'hoistValue',
},
],
},
cache,
pubsub,
baseDir,
apiName: '',
importFn,
});
const schema = makeExecutableSchema({
typeDefs: schemaDefs,
});
const transformedSchema = pruneSchema(transform.transformSchema(schema));
expect(transformedSchema.getType('BooksApiResponse')).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().code).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().isAvailable.type.toString()).toBe(
'Boolean'
);
expect(transformedSchema.getType('Author')).toBeUndefined();
expect((transformedSchema.getType('Book') as GraphQLObjectType).getFields().author.type.toString()).toBe('String!');
expect(printSchema(transformedSchema)).toMatchSnapshot();
});
});
Example #18
Source File: createGraphQLServer.ts From davinci with MIT License | 4 votes |
createGraphQLServer = (
app: DaVinciExpress,
controllers: ClassType[],
options?: ICreateGraphQLServerOptions
) => {
const { context, graphqlEndpoint, graphqlOptions, playgroundEnabled, playgroundOptions } = _.merge(
{},
DEFAULT_OPTIONS,
options
);
const allSchemas = { queries: {}, mutations: {}, schemas: {} };
const { queries: queryFields, mutations: mutationsFields } = (controllers || []).reduce(
(acc, controller) => {
_.merge(allSchemas, acc.schemas);
const { queries, mutations, schemas } = createControllerSchemas(controller, allSchemas);
if (queries) {
acc.queries = _.merge({}, acc.queries || {}, queries);
}
if (mutations) {
acc.mutations = _.merge(acc.mutations || {}, mutations);
}
if (schemas) {
acc.schemas = _.merge(acc.schemas || {}, schemas);
}
return acc;
},
{ queries: null, mutations: null, schemas: null }
);
const schema = new GraphQLSchema({
query: queryFields
? new GraphQLObjectType({
name: 'Query',
fields: {
...queryFields
}
})
: null,
mutation: !_.isEmpty(mutationsFields)
? new GraphQLObjectType({
name: 'Mutation',
fields: {
...mutationsFields
}
})
: null
});
if (playgroundEnabled) {
// tslint:disable-next-line:variable-name
app.get(graphqlEndpoint, (_req, res) => res.send(playground(playgroundOptions)));
}
app.use(
graphqlEndpoint,
graphqlHTTP(async request => {
const ctx = typeof context === 'function' ? await context(request) : context;
return {
schema,
graphiql: false,
context: ctx,
...(graphqlOptions || {})
};
})
);
console.info(`--- ? GraphQL running on ${graphqlEndpoint}`);
if (playgroundEnabled) {
console.info(`--- ? GraphQL Playground on ${graphqlEndpoint}`);
}
const printSDL = () => printSchema(schema);
const writeSDLtoFile = async path => {
const sdl = printSDL();
await fs.promises.writeFile(path, sdl);
};
return { app, schema, printSDL, writeSDLtoFile };
}
Example #19
Source File: schema.spec.ts From graphql-eslint with MIT License | 4 votes |
describe('schema', () => {
const SCHEMA_GRAPHQL_PATH = resolve(__dirname, 'mocks/user-schema.graphql');
const SCHEMA_CODE_PATH = resolve(__dirname, 'mocks/user-schema.ts');
const SCHEMA_JSON_PATH = resolve(__dirname, 'mocks/user-schema.json');
const schemaOnDisk = readFileSync(SCHEMA_GRAPHQL_PATH, 'utf8');
const testSchema = (schema: string) => {
const gqlConfig = loadGraphQLConfig({ schema });
const graphQLSchema = getSchema(gqlConfig.getDefault());
expect(graphQLSchema).toBeInstanceOf(GraphQLSchema);
const sdlString = printSchema(graphQLSchema as GraphQLSchema);
expect(sdlString.trim()).toBe(schemaOnDisk.trim());
};
describe('GraphQLFileLoader', () => {
it('should load schema from GraphQL file', () => {
testSchema(SCHEMA_GRAPHQL_PATH);
});
});
describe('CodeFileLoader', () => {
it('should load schema from code file', () => {
testSchema(SCHEMA_CODE_PATH);
});
});
describe('JsonFileLoader', () => {
it('should load schema from JSON file', () => {
testSchema(SCHEMA_JSON_PATH);
});
});
describe('UrlLoader', () => {
let local;
let url;
beforeAll(done => {
const tsNodeCommand = resolve(process.cwd(), 'node_modules/.bin/ts-node');
const serverPath = resolve(__dirname, 'mocks/graphql-server.ts');
// Import `TestGraphQLServer` and run it in this file will don't work
// because `@graphql-tools/url-loader` under the hood uses `sync-fetch` package that uses
// `child_process.execFileSync` that block Node.js event loop
local = spawn(tsNodeCommand, [serverPath]);
local.stdout.on('data', chunk => {
url = chunk.toString().trimRight();
done();
});
local.stderr.on('data', chunk => {
throw new Error(chunk.toString().trimRight());
});
});
afterAll(done => {
local.on('close', done);
local.kill();
});
it('should load schema from URL', () => {
testSchema(url);
});
describe('should passe headers', () => {
let schemaUrl;
let schemaOptions;
beforeAll(() => {
schemaUrl = `${url}/my-headers`;
schemaOptions = {
headers: {
authorization: 'Bearer Foo',
},
};
});
// https://graphql-config.com/schema#passing-headers
it('with `parserOptions.schema`', () => {
const gqlConfig = loadGraphQLConfig({
schema: {
[schemaUrl]: schemaOptions,
},
});
const error = getSchema(gqlConfig.getDefault()) as Error;
expect(error).toBeInstanceOf(Error);
expect(error.message).toMatch('"authorization":"Bearer Foo"');
});
// https://github.com/B2o5T/graphql-eslint/blob/master/docs/parser-options.md#schemaoptions
it('with `parserOptions.schemaOptions`', () => {
const gqlConfig = loadGraphQLConfig({ schema: schemaUrl });
const error = getSchema(gqlConfig.getDefault(), { schemaOptions }) as Error;
expect(error).toBeInstanceOf(Error);
expect(error.message).toMatch('"authorization":"Bearer Foo"');
});
});
});
describe('schema loading', () => {
it('should return Error', () => {
const gqlConfig = loadGraphQLConfig({ schema: 'not-exist.gql' });
const error = getSchema(gqlConfig.getDefault()) as Error;
expect(error).toBeInstanceOf(Error);
expect(error.message).toMatch('Unable to find any GraphQL type definitions for the following pointers');
});
});
it('should load the graphql-config rc file relative to the linted file', () => {
const schema = resolve(__dirname, 'mocks/using-config/schema.graphql');
const gqlConfig = loadGraphQLConfig({
schema,
filePath: resolve(__dirname, 'mocks/using-config/test.graphql'),
});
const graphQLSchema = getSchema(gqlConfig.getDefault()) as GraphQLSchema;
expect(graphQLSchema).toBeInstanceOf(GraphQLSchema);
const sdlString = printSchema(graphQLSchema);
expect(sdlString.trimEnd()).toMatchInlineSnapshot(/* GraphQL */ `
type Query {
hello: String
}
`);
});
});