@graphql-tools/mock 8.4.3 → 8.5.2-alpha-40340ed2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -0
- package/index.js +83 -14
- package/index.mjs +84 -16
- package/package.json +5 -4
- package/pagination.d.ts +86 -0
- package/utils.d.ts +2 -0
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -59,6 +59,10 @@ function copyOwnProps(target, ...sources) {
|
|
|
59
59
|
}
|
|
60
60
|
return target;
|
|
61
61
|
}
|
|
62
|
+
const isRootType = (type, schema) => {
|
|
63
|
+
const rootTypeNames = utils.getRootTypeNames(schema);
|
|
64
|
+
return rootTypeNames.has(type.name);
|
|
65
|
+
};
|
|
62
66
|
|
|
63
67
|
/**
|
|
64
68
|
* @internal
|
|
@@ -678,12 +682,12 @@ function addMocksToSchema({ schema: schema$1, store: maybeStore, mocks, typePoli
|
|
|
678
682
|
if (mocks && !isObject(mocks)) {
|
|
679
683
|
throw new Error('mocks must be of type Object');
|
|
680
684
|
}
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
685
|
+
const mockStore = createMockStore({
|
|
686
|
+
schema: schema$1,
|
|
687
|
+
mocks,
|
|
688
|
+
typePolicies,
|
|
689
|
+
});
|
|
690
|
+
const store = maybeStore || mockStore;
|
|
687
691
|
const resolvers = typeof resolversOrFnResolvers === 'function' ? resolversOrFnResolvers(store) : resolversOrFnResolvers;
|
|
688
692
|
const mockResolver = (source, args, contex, info) => {
|
|
689
693
|
const defaultResolvedValue = graphql.defaultFieldResolver(source, args, contex, info);
|
|
@@ -709,10 +713,8 @@ function addMocksToSchema({ schema: schema$1, store: maybeStore, mocks, typePoli
|
|
|
709
713
|
});
|
|
710
714
|
}
|
|
711
715
|
if (defaultResolvedValue === undefined) {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
return mockFn(source, args, contex, info);
|
|
715
|
-
}
|
|
716
|
+
// any is used here because generateFieldValue is a private method at time of writing
|
|
717
|
+
return mockStore.generateFieldValue(info.parentType.name, info.fieldName);
|
|
716
718
|
}
|
|
717
719
|
return undefined;
|
|
718
720
|
};
|
|
@@ -803,10 +805,6 @@ function addMocksToSchema({ schema: schema$1, store: maybeStore, mocks, typePoli
|
|
|
803
805
|
});
|
|
804
806
|
return resolvers ? schema.addResolversToSchema(schemaWithMocks, resolvers) : schemaWithMocks;
|
|
805
807
|
}
|
|
806
|
-
const isRootType = (type, schema) => {
|
|
807
|
-
const rootTypeNames = utils.getRootTypeNames(schema);
|
|
808
|
-
return rootTypeNames.has(type.name);
|
|
809
|
-
};
|
|
810
808
|
|
|
811
809
|
/**
|
|
812
810
|
* A convenience wrapper on top of addMocksToSchema. It adds your mock resolvers
|
|
@@ -841,6 +839,76 @@ function mockServer(schema$1, mocks, preserveResolvers = false) {
|
|
|
841
839
|
};
|
|
842
840
|
}
|
|
843
841
|
|
|
842
|
+
/**
|
|
843
|
+
* Produces a resolver that'll mock a [Relay-style cursor pagination](https://relay.dev/graphql/connections.htm).
|
|
844
|
+
*
|
|
845
|
+
* ```ts
|
|
846
|
+
* const schemaWithMocks = addMocksToSchema({
|
|
847
|
+
* schema,
|
|
848
|
+
* resolvers: (store) => ({
|
|
849
|
+
* User: {
|
|
850
|
+
* friends: relayStylePaginationMock(store),
|
|
851
|
+
* }
|
|
852
|
+
* }),
|
|
853
|
+
* })
|
|
854
|
+
* ```
|
|
855
|
+
* @param store the MockStore
|
|
856
|
+
*/
|
|
857
|
+
const relayStylePaginationMock = (store, { cursorFn = node => `${node.$ref.key}`, applyOnNodes, allNodesFn, } = {}) => {
|
|
858
|
+
return (parent, args, context, info) => {
|
|
859
|
+
const source = isRootType(info.parentType, info.schema) ? makeRef(info.parentType.name, 'ROOT') : parent;
|
|
860
|
+
const allNodesFn_ = allNodesFn !== null && allNodesFn !== void 0 ? allNodesFn : defaultAllNodesFn(store);
|
|
861
|
+
let allNodes = allNodesFn_(source, args, context, info);
|
|
862
|
+
if (applyOnNodes) {
|
|
863
|
+
allNodes = applyOnNodes(allNodes, args);
|
|
864
|
+
}
|
|
865
|
+
const allEdges = allNodes.map(node => ({
|
|
866
|
+
node,
|
|
867
|
+
cursor: cursorFn(node),
|
|
868
|
+
}));
|
|
869
|
+
let start, end;
|
|
870
|
+
const { first, after, last, before } = args;
|
|
871
|
+
if (typeof first === 'number') {
|
|
872
|
+
// forward pagination
|
|
873
|
+
if (last || before) {
|
|
874
|
+
throw new Error("if `first` is provided, `last` or `before` can't be provided");
|
|
875
|
+
}
|
|
876
|
+
const afterIndex = after ? allEdges.findIndex(e => e.cursor === after) : -1;
|
|
877
|
+
start = afterIndex + 1;
|
|
878
|
+
end = afterIndex + 1 + first;
|
|
879
|
+
}
|
|
880
|
+
else if (typeof last === 'number') {
|
|
881
|
+
// backward pagination
|
|
882
|
+
if (first || after) {
|
|
883
|
+
throw new Error("if `last` is provided, `first` or `after` can't be provided");
|
|
884
|
+
}
|
|
885
|
+
const foundBeforeIndex = before ? allEdges.findIndex(e => e.cursor === before) : -1;
|
|
886
|
+
const beforeIndex = foundBeforeIndex !== -1 ? foundBeforeIndex : allNodes.length;
|
|
887
|
+
start = allEdges.length - (allEdges.length - beforeIndex) - last;
|
|
888
|
+
// negative index on Array.slice indicate offset from end of sequence => we don't want
|
|
889
|
+
if (start < 0)
|
|
890
|
+
start = 0;
|
|
891
|
+
end = beforeIndex;
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
throw new Error('A `first` or a `last` arguments should be provided');
|
|
895
|
+
}
|
|
896
|
+
const edges = allEdges.slice(start, end);
|
|
897
|
+
const pageInfo = {
|
|
898
|
+
startCursor: edges.length > 0 ? edges[0].cursor : '',
|
|
899
|
+
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : '',
|
|
900
|
+
hasNextPage: end < allEdges.length - 1,
|
|
901
|
+
hasPreviousPage: start > 0,
|
|
902
|
+
};
|
|
903
|
+
return {
|
|
904
|
+
edges,
|
|
905
|
+
pageInfo,
|
|
906
|
+
totalCount: allEdges.length,
|
|
907
|
+
};
|
|
908
|
+
};
|
|
909
|
+
};
|
|
910
|
+
const defaultAllNodesFn = (store) => (parent, _, __, info) => store.get(parent, [info.fieldName, 'edges']).map(e => store.get(e, 'node'));
|
|
911
|
+
|
|
844
912
|
exports.MockList = MockList;
|
|
845
913
|
exports.MockStore = MockStore;
|
|
846
914
|
exports.addMocksToSchema = addMocksToSchema;
|
|
@@ -852,3 +920,4 @@ exports.isMockList = isMockList;
|
|
|
852
920
|
exports.isRecord = isRecord;
|
|
853
921
|
exports.isRef = isRef;
|
|
854
922
|
exports.mockServer = mockServer;
|
|
923
|
+
exports.relayStylePaginationMock = relayStylePaginationMock;
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isNullableType, getNullableType, isCompositeType, isAbstractType, isListType, isScalarType, isEnumType, isObjectType, GraphQLString, isInterfaceType, isSchema, isUnionType, GraphQLUnionType, GraphQLInterfaceType, defaultFieldResolver, graphql } from 'graphql';
|
|
2
2
|
import stringify from 'fast-json-stable-stringify';
|
|
3
|
-
import { mapSchema, MapperKind
|
|
3
|
+
import { getRootTypeNames, mapSchema, MapperKind } from '@graphql-tools/utils';
|
|
4
4
|
import { addResolversToSchema, makeExecutableSchema } from '@graphql-tools/schema';
|
|
5
5
|
|
|
6
6
|
function isRef(maybeRef) {
|
|
@@ -53,6 +53,10 @@ function copyOwnProps(target, ...sources) {
|
|
|
53
53
|
}
|
|
54
54
|
return target;
|
|
55
55
|
}
|
|
56
|
+
const isRootType = (type, schema) => {
|
|
57
|
+
const rootTypeNames = getRootTypeNames(schema);
|
|
58
|
+
return rootTypeNames.has(type.name);
|
|
59
|
+
};
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
62
|
* @internal
|
|
@@ -672,12 +676,12 @@ function addMocksToSchema({ schema, store: maybeStore, mocks, typePolicies, reso
|
|
|
672
676
|
if (mocks && !isObject(mocks)) {
|
|
673
677
|
throw new Error('mocks must be of type Object');
|
|
674
678
|
}
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
679
|
+
const mockStore = createMockStore({
|
|
680
|
+
schema,
|
|
681
|
+
mocks,
|
|
682
|
+
typePolicies,
|
|
683
|
+
});
|
|
684
|
+
const store = maybeStore || mockStore;
|
|
681
685
|
const resolvers = typeof resolversOrFnResolvers === 'function' ? resolversOrFnResolvers(store) : resolversOrFnResolvers;
|
|
682
686
|
const mockResolver = (source, args, contex, info) => {
|
|
683
687
|
const defaultResolvedValue = defaultFieldResolver(source, args, contex, info);
|
|
@@ -703,10 +707,8 @@ function addMocksToSchema({ schema, store: maybeStore, mocks, typePolicies, reso
|
|
|
703
707
|
});
|
|
704
708
|
}
|
|
705
709
|
if (defaultResolvedValue === undefined) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return mockFn(source, args, contex, info);
|
|
709
|
-
}
|
|
710
|
+
// any is used here because generateFieldValue is a private method at time of writing
|
|
711
|
+
return mockStore.generateFieldValue(info.parentType.name, info.fieldName);
|
|
710
712
|
}
|
|
711
713
|
return undefined;
|
|
712
714
|
};
|
|
@@ -797,10 +799,6 @@ function addMocksToSchema({ schema, store: maybeStore, mocks, typePolicies, reso
|
|
|
797
799
|
});
|
|
798
800
|
return resolvers ? addResolversToSchema(schemaWithMocks, resolvers) : schemaWithMocks;
|
|
799
801
|
}
|
|
800
|
-
const isRootType = (type, schema) => {
|
|
801
|
-
const rootTypeNames = getRootTypeNames(schema);
|
|
802
|
-
return rootTypeNames.has(type.name);
|
|
803
|
-
};
|
|
804
802
|
|
|
805
803
|
/**
|
|
806
804
|
* A convenience wrapper on top of addMocksToSchema. It adds your mock resolvers
|
|
@@ -835,4 +833,74 @@ function mockServer(schema, mocks, preserveResolvers = false) {
|
|
|
835
833
|
};
|
|
836
834
|
}
|
|
837
835
|
|
|
838
|
-
|
|
836
|
+
/**
|
|
837
|
+
* Produces a resolver that'll mock a [Relay-style cursor pagination](https://relay.dev/graphql/connections.htm).
|
|
838
|
+
*
|
|
839
|
+
* ```ts
|
|
840
|
+
* const schemaWithMocks = addMocksToSchema({
|
|
841
|
+
* schema,
|
|
842
|
+
* resolvers: (store) => ({
|
|
843
|
+
* User: {
|
|
844
|
+
* friends: relayStylePaginationMock(store),
|
|
845
|
+
* }
|
|
846
|
+
* }),
|
|
847
|
+
* })
|
|
848
|
+
* ```
|
|
849
|
+
* @param store the MockStore
|
|
850
|
+
*/
|
|
851
|
+
const relayStylePaginationMock = (store, { cursorFn = node => `${node.$ref.key}`, applyOnNodes, allNodesFn, } = {}) => {
|
|
852
|
+
return (parent, args, context, info) => {
|
|
853
|
+
const source = isRootType(info.parentType, info.schema) ? makeRef(info.parentType.name, 'ROOT') : parent;
|
|
854
|
+
const allNodesFn_ = allNodesFn !== null && allNodesFn !== void 0 ? allNodesFn : defaultAllNodesFn(store);
|
|
855
|
+
let allNodes = allNodesFn_(source, args, context, info);
|
|
856
|
+
if (applyOnNodes) {
|
|
857
|
+
allNodes = applyOnNodes(allNodes, args);
|
|
858
|
+
}
|
|
859
|
+
const allEdges = allNodes.map(node => ({
|
|
860
|
+
node,
|
|
861
|
+
cursor: cursorFn(node),
|
|
862
|
+
}));
|
|
863
|
+
let start, end;
|
|
864
|
+
const { first, after, last, before } = args;
|
|
865
|
+
if (typeof first === 'number') {
|
|
866
|
+
// forward pagination
|
|
867
|
+
if (last || before) {
|
|
868
|
+
throw new Error("if `first` is provided, `last` or `before` can't be provided");
|
|
869
|
+
}
|
|
870
|
+
const afterIndex = after ? allEdges.findIndex(e => e.cursor === after) : -1;
|
|
871
|
+
start = afterIndex + 1;
|
|
872
|
+
end = afterIndex + 1 + first;
|
|
873
|
+
}
|
|
874
|
+
else if (typeof last === 'number') {
|
|
875
|
+
// backward pagination
|
|
876
|
+
if (first || after) {
|
|
877
|
+
throw new Error("if `last` is provided, `first` or `after` can't be provided");
|
|
878
|
+
}
|
|
879
|
+
const foundBeforeIndex = before ? allEdges.findIndex(e => e.cursor === before) : -1;
|
|
880
|
+
const beforeIndex = foundBeforeIndex !== -1 ? foundBeforeIndex : allNodes.length;
|
|
881
|
+
start = allEdges.length - (allEdges.length - beforeIndex) - last;
|
|
882
|
+
// negative index on Array.slice indicate offset from end of sequence => we don't want
|
|
883
|
+
if (start < 0)
|
|
884
|
+
start = 0;
|
|
885
|
+
end = beforeIndex;
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
throw new Error('A `first` or a `last` arguments should be provided');
|
|
889
|
+
}
|
|
890
|
+
const edges = allEdges.slice(start, end);
|
|
891
|
+
const pageInfo = {
|
|
892
|
+
startCursor: edges.length > 0 ? edges[0].cursor : '',
|
|
893
|
+
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : '',
|
|
894
|
+
hasNextPage: end < allEdges.length - 1,
|
|
895
|
+
hasPreviousPage: start > 0,
|
|
896
|
+
};
|
|
897
|
+
return {
|
|
898
|
+
edges,
|
|
899
|
+
pageInfo,
|
|
900
|
+
totalCount: allEdges.length,
|
|
901
|
+
};
|
|
902
|
+
};
|
|
903
|
+
};
|
|
904
|
+
const defaultAllNodesFn = (store) => (parent, _, __, info) => store.get(parent, [info.fieldName, 'edges']).map(e => store.get(e, 'node'));
|
|
905
|
+
|
|
906
|
+
export { MockList, MockStore, addMocksToSchema, assertIsRef, createMockStore, deepResolveMockList, defaultMocks, isMockList, isRecord, isRef, mockServer, relayStylePaginationMock };
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-tools/mock",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.5.2-alpha-40340ed2.0",
|
|
4
4
|
"description": "A set of utils for faster development of GraphQL tools",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@graphql-tools/schema": "
|
|
11
|
-
"@graphql-tools/utils": "
|
|
10
|
+
"@graphql-tools/schema": "8.3.2-alpha-40340ed2.0",
|
|
11
|
+
"@graphql-tools/utils": "8.6.1-alpha-40340ed2.0",
|
|
12
12
|
"fast-json-stable-stringify": "^2.1.0",
|
|
13
13
|
"tslib": "~2.3.0"
|
|
14
14
|
},
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"./*": {
|
|
33
33
|
"require": "./*.js",
|
|
34
34
|
"import": "./*.mjs"
|
|
35
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
36
37
|
}
|
|
37
38
|
}
|
package/pagination.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { IFieldResolver } from '@graphql-tools/utils';
|
|
2
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
3
|
+
import { IMockStore, Ref } from './types';
|
|
4
|
+
export declare type AllNodesFn<TContext, TArgs extends RelayPaginationParams> = (parent: Ref, args: TArgs, context: TContext, info: GraphQLResolveInfo) => Ref[];
|
|
5
|
+
export declare type RelayStylePaginationMockOptions<TContext, TArgs extends RelayPaginationParams> = {
|
|
6
|
+
/**
|
|
7
|
+
* Use this option to apply filtering or sorting on the nodes given the
|
|
8
|
+
* arguments the paginated field receives.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* {
|
|
12
|
+
* User: {
|
|
13
|
+
* friends: mockedRelayStylePagination<
|
|
14
|
+
* unknown,
|
|
15
|
+
* RelayPaginationParams & { sortByBirthdateDesc?: boolean}
|
|
16
|
+
* >(
|
|
17
|
+
* store, {
|
|
18
|
+
* applyOnEdges: (edges, { sortByBirthdateDesc }) => {
|
|
19
|
+
* if (!sortByBirthdateDesc) return edges
|
|
20
|
+
* return _.sortBy(edges, (e) => store.get(e, ['node', 'birthdate']))
|
|
21
|
+
* }
|
|
22
|
+
* }),
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
applyOnNodes?: (nodeRefs: Ref[], args: TArgs) => Ref[];
|
|
28
|
+
/**
|
|
29
|
+
* A function that'll be used to get all the nodes used for pagination.
|
|
30
|
+
*
|
|
31
|
+
* By default, it will use the nodes of the field this pagination is attached to.
|
|
32
|
+
*
|
|
33
|
+
* This option is handy when several paginable fields should share
|
|
34
|
+
* the same base nodes:
|
|
35
|
+
* ```ts
|
|
36
|
+
* {
|
|
37
|
+
* User: {
|
|
38
|
+
* friends: mockedRelayStylePagination(store),
|
|
39
|
+
* maleFriends: mockedRelayStylePagination(store, {
|
|
40
|
+
* allNodesFn: (userRef) =>
|
|
41
|
+
* store
|
|
42
|
+
* .get(userRef, ['friends', 'edges'])
|
|
43
|
+
* .map((e) => store.get(e, 'node'))
|
|
44
|
+
* .filter((userRef) => store.get(userRef, 'sex') === 'male')
|
|
45
|
+
* })
|
|
46
|
+
* }
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
allNodesFn?: AllNodesFn<TContext, TArgs>;
|
|
51
|
+
/**
|
|
52
|
+
* The function that'll be used to compute the cursor of a node.
|
|
53
|
+
*
|
|
54
|
+
* By default, it'll use `MockStore` internal reference `Ref`'s `key`
|
|
55
|
+
* as cursor.
|
|
56
|
+
*/
|
|
57
|
+
cursorFn?: (nodeRef: Ref) => string;
|
|
58
|
+
};
|
|
59
|
+
export declare type RelayPaginationParams = {
|
|
60
|
+
first?: number;
|
|
61
|
+
after?: string;
|
|
62
|
+
last?: number;
|
|
63
|
+
before?: string;
|
|
64
|
+
};
|
|
65
|
+
export declare type RelayPageInfo = {
|
|
66
|
+
hasPreviousPage: boolean;
|
|
67
|
+
hasNextPage: boolean;
|
|
68
|
+
startCursor: string;
|
|
69
|
+
endCursor: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Produces a resolver that'll mock a [Relay-style cursor pagination](https://relay.dev/graphql/connections.htm).
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* const schemaWithMocks = addMocksToSchema({
|
|
76
|
+
* schema,
|
|
77
|
+
* resolvers: (store) => ({
|
|
78
|
+
* User: {
|
|
79
|
+
* friends: relayStylePaginationMock(store),
|
|
80
|
+
* }
|
|
81
|
+
* }),
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
84
|
+
* @param store the MockStore
|
|
85
|
+
*/
|
|
86
|
+
export declare const relayStylePaginationMock: <TContext, TArgs extends RelayPaginationParams = RelayPaginationParams>(store: IMockStore, { cursorFn, applyOnNodes, allNodesFn, }?: RelayStylePaginationMockOptions<TContext, TArgs>) => IFieldResolver<Ref<string>, TContext, TArgs, any>;
|
package/utils.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GraphQLObjectType, GraphQLSchema } from 'graphql';
|
|
1
2
|
import { Ref, KeyTypeConstraints } from './types';
|
|
2
3
|
export declare function uuidv4(): string;
|
|
3
4
|
export declare const randomListLength: () => number;
|
|
@@ -6,3 +7,4 @@ export declare function makeRef<KeyT extends KeyTypeConstraints = string>(typeNa
|
|
|
6
7
|
export declare function isObject(thing: any): boolean;
|
|
7
8
|
export declare function copyOwnPropsIfNotPresent(target: Record<string, any>, source: Record<string, any>): void;
|
|
8
9
|
export declare function copyOwnProps(target: Record<string, any>, ...sources: Array<Record<string, any>>): Record<string, any>;
|
|
10
|
+
export declare const isRootType: (type: GraphQLObjectType, schema: GraphQLSchema) => boolean;
|