mirror of
https://github.com/vale981/apollo-server
synced 2025-03-05 17:51:40 -05:00
implement operation store
This commit is contained in:
parent
a5df5bffeb
commit
0426452f93
3 changed files with 162 additions and 0 deletions
113
src/modules/operationStore.test.ts
Normal file
113
src/modules/operationStore.test.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import {
|
||||
expect,
|
||||
} from 'chai';
|
||||
|
||||
import {
|
||||
GraphQLSchema,
|
||||
GraphQLObjectType,
|
||||
GraphQLString,
|
||||
GraphQLInt,
|
||||
GraphQLNonNull,
|
||||
print,
|
||||
} from 'graphql';
|
||||
|
||||
import { OperationStore } from './operationStore';
|
||||
|
||||
const QueryType = new GraphQLObjectType({
|
||||
name: 'QueryType',
|
||||
fields: {
|
||||
testString: {
|
||||
type: GraphQLString,
|
||||
resolve() {
|
||||
return 'it works';
|
||||
},
|
||||
},
|
||||
testRootValue: {
|
||||
type: GraphQLString,
|
||||
resolve(root) {
|
||||
return root + ' works';
|
||||
},
|
||||
},
|
||||
testContextValue: {
|
||||
type: GraphQLString,
|
||||
resolve(root, args, context) {
|
||||
return context + ' works';
|
||||
},
|
||||
},
|
||||
testArgumentValue: {
|
||||
type: GraphQLInt,
|
||||
resolve(root, args, context) {
|
||||
return args['base'] + 5;
|
||||
},
|
||||
args: {
|
||||
base: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Schema = new GraphQLSchema({
|
||||
query: QueryType,
|
||||
});
|
||||
|
||||
describe('operationStore', () => {
|
||||
it('can store a query and return its ast', () => {
|
||||
const query = `{ testString }`;
|
||||
const expected = `{\n testString\n}\n`;
|
||||
|
||||
const store = new OperationStore(Schema);
|
||||
store.put('testquery', query);
|
||||
|
||||
return expect(print(store.get('testquery'))).to.deep.equal(expected);
|
||||
});
|
||||
|
||||
it('throws a parse error if the query is invalid', () => {
|
||||
const query = `{ testString`;
|
||||
|
||||
const store = new OperationStore(Schema);
|
||||
return expect(() => store.put('testquery', query)).to.throw(/found EOF/);
|
||||
});
|
||||
|
||||
it('throws a validation error if the query is invalid', () => {
|
||||
const query = `{ testStrin }`;
|
||||
|
||||
const store = new OperationStore(Schema);
|
||||
return expect(() => store.put('testquery', query)).to.throw(/Cannot query field/);
|
||||
});
|
||||
|
||||
it('throws an error if there is more than one query or mutation', () => {
|
||||
const query = `
|
||||
query Q1{ testString }
|
||||
query Q2{ t2: testString }
|
||||
`;
|
||||
|
||||
const store = new OperationStore(Schema);
|
||||
return expect(() => store.put('testquery', query)).to.throw(/operationDefinition must contain only one definition/);
|
||||
});
|
||||
|
||||
it('throws an error if there is no operationDefinition found', () => {
|
||||
const query = `
|
||||
type Q {
|
||||
f1: Int
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Q
|
||||
}
|
||||
`;
|
||||
|
||||
const store = new OperationStore(Schema);
|
||||
|
||||
return expect(() => store.put('testquery', query)).to.throw(/must contain an OperationDefintion/);
|
||||
});
|
||||
|
||||
it('can delete stored operations', () => {
|
||||
const query = `{ testString }`;
|
||||
|
||||
const store = new OperationStore(Schema);
|
||||
store.put('testquery', query);
|
||||
store.delete('testquery');
|
||||
|
||||
return expect(store.get('testquery')).to.be.undefined;
|
||||
});
|
||||
});
|
48
src/modules/operationStore.ts
Normal file
48
src/modules/operationStore.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import {
|
||||
parse,
|
||||
validate,
|
||||
Document,
|
||||
GraphQLSchema,
|
||||
} from 'graphql';
|
||||
|
||||
export class OperationStore {
|
||||
private storedOperations: Map<string, Document>;
|
||||
private schema: GraphQLSchema;
|
||||
|
||||
constructor(schema: GraphQLSchema) {
|
||||
this.schema = schema;
|
||||
this.storedOperations = new Map();
|
||||
}
|
||||
|
||||
// TODO: maybe we should extract the operationName from the operationDefinition?
|
||||
public put(operationName: string, operationDefinition: string) {
|
||||
const ast = parse(operationDefinition);
|
||||
const kind = ast.definitions[0].kind;
|
||||
if (kind !== 'OperationDefinition') {
|
||||
throw new Error(`operationDefinition must contain an OperationDefintion, got ${kind}: ${operationDefinition}`);
|
||||
}
|
||||
if (ast.definitions.length > 1) {
|
||||
throw new Error('operationDefinition must contain only one definition');
|
||||
}
|
||||
const validationErrors = validate(this.schema, ast);
|
||||
if (validationErrors.length > 0) {
|
||||
const messages = validationErrors.map((e) => e.message);
|
||||
const e = new Error(`Validation Errors:\n${messages.join('\n')}`);
|
||||
e['originalErrors'] = validationErrors;
|
||||
throw e;
|
||||
}
|
||||
this.storedOperations.set(operationName, ast);
|
||||
}
|
||||
|
||||
public get(operationName: string) {
|
||||
return this.storedOperations.get(operationName);
|
||||
}
|
||||
|
||||
public delete(operationName: string) {
|
||||
return this.storedOperations.delete(operationName);
|
||||
}
|
||||
|
||||
public getMap() {
|
||||
return this.storedOperations;
|
||||
}
|
||||
}
|
|
@ -8,5 +8,6 @@ declare function require(name: string);
|
|||
require('source-map-support').install();
|
||||
|
||||
import '../core/runQuery.test';
|
||||
import '../modules/operationStore.test';
|
||||
import '../integrations/expressApollo.test';
|
||||
import './testApolloServerHTTP';
|
||||
|
|
Loading…
Add table
Reference in a new issue