implement operation store

This commit is contained in:
Jonas Helfer 2016-06-28 15:34:13 -04:00
parent a5df5bffeb
commit 0426452f93
3 changed files with 162 additions and 0 deletions

View 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;
});
});

View 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;
}
}

View file

@ -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';