123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- Object.defineProperty(exports, "__esModule", { value: true });
- var graphql_1 = require("graphql");
- var uuid = require("uuid");
- var makeExecutableSchema_1 = require("./makeExecutableSchema");
- // This function wraps addMockFunctionsToSchema for more convenience
- function mockServer(schema, mocks, preserveResolvers) {
- if (preserveResolvers === void 0) { preserveResolvers = false; }
- var mySchema;
- if (!(schema instanceof graphql_1.GraphQLSchema)) {
- // TODO: provide useful error messages here if this fails
- mySchema = makeExecutableSchema_1.buildSchemaFromTypeDefinitions(schema);
- }
- else {
- mySchema = schema;
- }
- addMockFunctionsToSchema({ schema: mySchema, mocks: mocks, preserveResolvers: preserveResolvers });
- return { query: function (query, vars) { return graphql_1.graphql(mySchema, query, {}, {}, vars); } };
- }
- exports.mockServer = mockServer;
- var defaultMockMap = new Map();
- defaultMockMap.set('Int', function () { return Math.round(Math.random() * 200) - 100; });
- defaultMockMap.set('Float', function () { return Math.random() * 200 - 100; });
- defaultMockMap.set('String', function () { return 'Hello World'; });
- defaultMockMap.set('Boolean', function () { return Math.random() > 0.5; });
- defaultMockMap.set('ID', function () { return uuid.v4(); });
- // TODO allow providing a seed such that lengths of list could be deterministic
- // this could be done by using casual to get a random list length if the casual
- // object is global.
- function addMockFunctionsToSchema(_a) {
- var schema = _a.schema, _b = _a.mocks, mocks = _b === void 0 ? {} : _b, _c = _a.preserveResolvers, preserveResolvers = _c === void 0 ? false : _c;
- if (!schema) {
- throw new Error('Must provide schema to mock');
- }
- if (!(schema instanceof graphql_1.GraphQLSchema)) {
- throw new Error('Value at "schema" must be of type GraphQLSchema');
- }
- if (!isObject(mocks)) {
- throw new Error('mocks must be of type Object');
- }
- // use Map internally, because that API is nicer.
- var mockFunctionMap = new Map();
- Object.keys(mocks).forEach(function (typeName) {
- mockFunctionMap.set(typeName, mocks[typeName]);
- });
- mockFunctionMap.forEach(function (mockFunction, mockTypeName) {
- if (typeof mockFunction !== 'function') {
- throw new Error("mockFunctionMap[" + mockTypeName + "] must be a function");
- }
- });
- var mockType = function (type, typeName, fieldName) {
- // order of precendence for mocking:
- // 1. if the object passed in already has fieldName, just use that
- // --> if it's a function, that becomes your resolver
- // --> if it's a value, the mock resolver will return that
- // 2. if the nullableType is a list, recurse
- // 2. if there's a mock defined for this typeName, that will be used
- // 3. if there's no mock defined, use the default mocks for this type
- return function (root, args, context, info) {
- // nullability doesn't matter for the purpose of mocking.
- var fieldType = graphql_1.getNullableType(type);
- var namedFieldType = graphql_1.getNamedType(fieldType);
- if (root && typeof root[fieldName] !== 'undefined') {
- var result = void 0;
- // if we're here, the field is already defined
- if (typeof root[fieldName] === 'function') {
- result = root[fieldName](root, args, context, info);
- if (result instanceof MockList) {
- result = result.mock(root, args, context, info, fieldType, mockType);
- }
- }
- else {
- result = root[fieldName];
- }
- // Now we merge the result with the default mock for this type.
- // This allows overriding defaults while writing very little code.
- if (mockFunctionMap.has(namedFieldType.name)) {
- result = mergeMocks(mockFunctionMap
- .get(namedFieldType.name)
- .bind(null, root, args, context, info), result);
- }
- return result;
- }
- if (fieldType instanceof graphql_1.GraphQLList ||
- fieldType instanceof graphql_1.GraphQLNonNull) {
- return [
- mockType(fieldType.ofType)(root, args, context, info),
- mockType(fieldType.ofType)(root, args, context, info),
- ];
- }
- if (mockFunctionMap.has(fieldType.name) &&
- !(fieldType instanceof graphql_1.GraphQLUnionType ||
- fieldType instanceof graphql_1.GraphQLInterfaceType)) {
- // the object passed doesn't have this field, so we apply the default mock
- return mockFunctionMap.get(fieldType.name)(root, args, context, info);
- }
- if (fieldType instanceof graphql_1.GraphQLObjectType) {
- // objects don't return actual data, we only need to mock scalars!
- return {};
- }
- // if a mock function is provided for unionType or interfaceType, execute it to resolve the concrete type
- // otherwise randomly pick a type from all implementation types
- if (fieldType instanceof graphql_1.GraphQLUnionType ||
- fieldType instanceof graphql_1.GraphQLInterfaceType) {
- var implementationType = void 0;
- if (mockFunctionMap.has(fieldType.name)) {
- var interfaceMockObj = mockFunctionMap.get(fieldType.name)(root, args, context, info);
- if (!interfaceMockObj || !interfaceMockObj.__typename) {
- return Error("Please return a __typename in \"" + fieldType.name + "\"");
- }
- implementationType = schema.getType(interfaceMockObj.__typename);
- }
- else {
- var possibleTypes = schema.getPossibleTypes(fieldType);
- implementationType = getRandomElement(possibleTypes);
- }
- return Object.assign({ __typename: implementationType }, mockType(implementationType)(root, args, context, info));
- }
- if (fieldType instanceof graphql_1.GraphQLEnumType) {
- return getRandomElement(fieldType.getValues()).value;
- }
- if (defaultMockMap.has(fieldType.name)) {
- return defaultMockMap.get(fieldType.name)(root, args, context, info);
- }
- // if we get to here, we don't have a value, and we don't have a mock for this type,
- // we could return undefined, but that would be hard to debug, so we throw instead.
- // however, we returning it instead of throwing it, so preserveResolvers can handle the failures.
- return Error("No mock defined for type \"" + fieldType.name + "\"");
- };
- };
- makeExecutableSchema_1.forEachField(schema, function (field, typeName, fieldName) {
- assignResolveType(field.type, preserveResolvers);
- var mockResolver;
- // we have to handle the root mutation and root query types differently,
- // because no resolver is called at the root.
- /* istanbul ignore next: Must provide schema DefinitionNode with query type or a type named Query. */
- var isOnQueryType = schema.getQueryType() && schema.getQueryType().name === typeName;
- var isOnMutationType = schema.getMutationType() && schema.getMutationType().name === typeName;
- if (isOnQueryType || isOnMutationType) {
- if (mockFunctionMap.has(typeName)) {
- var rootMock_1 = mockFunctionMap.get(typeName);
- // XXX: BUG in here, need to provide proper signature for rootMock.
- if (typeof rootMock_1(undefined, {}, {}, {})[fieldName] === 'function') {
- mockResolver = function (root, args, context, info) {
- var updatedRoot = root || {}; // TODO: should we clone instead?
- updatedRoot[fieldName] = rootMock_1(root, args, context, info)[fieldName];
- // XXX this is a bit of a hack to still use mockType, which
- // lets you mock lists etc. as well
- // otherwise we could just set field.resolve to rootMock()[fieldName]
- // it's like pretending there was a resolve function that ran before
- // the root resolve function.
- return mockType(field.type, typeName, fieldName)(updatedRoot, args, context, info);
- };
- }
- }
- }
- if (!mockResolver) {
- mockResolver = mockType(field.type, typeName, fieldName);
- }
- if (!preserveResolvers || !field.resolve) {
- field.resolve = mockResolver;
- }
- else {
- var oldResolver_1 = field.resolve;
- field.resolve = function (rootObject, args, context, info) {
- return Promise.all([
- mockResolver(rootObject, args, context, info),
- oldResolver_1(rootObject, args, context, info),
- ]).then(function (values) {
- var mockedValue = values[0], resolvedValue = values[1];
- // In case we couldn't mock
- if (mockedValue instanceof Error) {
- // only if value was not resolved, populate the error.
- if (undefined === resolvedValue) {
- throw mockedValue;
- }
- return resolvedValue;
- }
- if (resolvedValue instanceof Date && mockedValue instanceof Date) {
- return undefined !== resolvedValue ? resolvedValue : mockedValue;
- }
- if (isObject(mockedValue) && isObject(resolvedValue)) {
- // Object.assign() won't do here, as we need to all properties, including
- // the non-enumerable ones and defined using Object.defineProperty
- var emptyObject = Object.create(Object.getPrototypeOf(resolvedValue));
- return copyOwnProps(emptyObject, resolvedValue, mockedValue);
- }
- return undefined !== resolvedValue ? resolvedValue : mockedValue;
- });
- };
- }
- });
- }
- exports.addMockFunctionsToSchema = addMockFunctionsToSchema;
- function isObject(thing) {
- return thing === Object(thing) && !Array.isArray(thing);
- }
- // returns a random element from that ary
- function getRandomElement(ary) {
- var sample = Math.floor(Math.random() * ary.length);
- return ary[sample];
- }
- function mergeObjects(a, b) {
- return Object.assign(a, b);
- }
- function copyOwnPropsIfNotPresent(target, source) {
- Object.getOwnPropertyNames(source).forEach(function (prop) {
- if (!Object.getOwnPropertyDescriptor(target, prop)) {
- Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop));
- }
- });
- }
- function copyOwnProps(target) {
- var sources = [];
- for (var _i = 1; _i < arguments.length; _i++) {
- sources[_i - 1] = arguments[_i];
- }
- sources.forEach(function (source) {
- var chain = source;
- while (chain) {
- copyOwnPropsIfNotPresent(target, chain);
- chain = Object.getPrototypeOf(chain);
- }
- });
- return target;
- }
- // takes either an object or a (possibly nested) array
- // and completes the customMock object with any fields
- // defined on genericMock
- // only merges objects or arrays. Scalars are returned as is
- function mergeMocks(genericMockFunction, customMock) {
- if (Array.isArray(customMock)) {
- return customMock.map(function (el) { return mergeMocks(genericMockFunction, el); });
- }
- if (isObject(customMock)) {
- return mergeObjects(genericMockFunction(), customMock);
- }
- return customMock;
- }
- function getResolveType(namedFieldType) {
- if (namedFieldType instanceof graphql_1.GraphQLInterfaceType ||
- namedFieldType instanceof graphql_1.GraphQLUnionType) {
- return namedFieldType.resolveType;
- }
- else {
- return undefined;
- }
- }
- function assignResolveType(type, preserveResolvers) {
- var fieldType = graphql_1.getNullableType(type);
- var namedFieldType = graphql_1.getNamedType(fieldType);
- var oldResolveType = getResolveType(namedFieldType);
- if (preserveResolvers && oldResolveType && oldResolveType.length) {
- return;
- }
- if (namedFieldType instanceof graphql_1.GraphQLUnionType ||
- namedFieldType instanceof graphql_1.GraphQLInterfaceType) {
- // the default `resolveType` always returns null. We add a fallback
- // resolution that works with how unions and interface are mocked
- namedFieldType.resolveType = function (data, context, info) {
- return info.schema.getType(data.__typename);
- };
- }
- }
- var MockList = /** @class */ (function () {
- // wrappedFunction can return another MockList or a value
- function MockList(len, wrappedFunction) {
- this.len = len;
- if (typeof wrappedFunction !== 'undefined') {
- if (typeof wrappedFunction !== 'function') {
- throw new Error('Second argument to MockList must be a function or undefined');
- }
- this.wrappedFunction = wrappedFunction;
- }
- }
- MockList.prototype.mock = function (root, args, context, info, fieldType, mockTypeFunc) {
- var arr;
- if (Array.isArray(this.len)) {
- arr = new Array(this.randint(this.len[0], this.len[1]));
- }
- else {
- arr = new Array(this.len);
- }
- for (var i = 0; i < arr.length; i++) {
- if (typeof this.wrappedFunction === 'function') {
- var res = this.wrappedFunction(root, args, context, info);
- if (res instanceof MockList) {
- var nullableType = graphql_1.getNullableType(fieldType.ofType);
- arr[i] = res.mock(root, args, context, info, nullableType, mockTypeFunc);
- }
- else {
- arr[i] = res;
- }
- }
- else {
- arr[i] = mockTypeFunc(fieldType.ofType)(root, args, context, info);
- }
- }
- return arr;
- };
- MockList.prototype.randint = function (low, high) {
- return Math.floor(Math.random() * (high - low + 1) + low);
- };
- return MockList;
- }());
- exports.MockList = MockList;
- //# sourceMappingURL=mock.js.map
|