Skip to content

Commit 995ec07

Browse files
committed
Finalize ConnectionResolver
1 parent 99b0a7c commit 995ec07

File tree

8 files changed

+108
-98
lines changed

8 files changed

+108
-98
lines changed

src/composeWithConnection.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ export function composeWithConnection(
2929

3030
typeComposer.addResolver(resolver);
3131
return typeComposer;
32+
}

src/connectionResolver.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable */
12
import { cursorToId, idToCursor, dotObject } from '../utils';
23
import { projectionCollection, projectionFixRenamed } from './projection';
34
import {

src/cursorId.js

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/definition.js

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,62 @@
11
/* @flow */
22
/* eslint-disable */
33

4-
export type CursorData = {
4+
import type {
5+
ResolveParams as _ResolveParams,
6+
} from 'graphql-compose/lib/definition.js';
7+
8+
export type ResolveParams = _ResolveParams;
9+
10+
export type ConnectionResolveParams = {
11+
source: mixed,
12+
args: {
13+
first?: ?number,
14+
after?: ?string,
15+
last?: ?number,
16+
before?: ?string,
17+
sort: connectionSortOpts,
18+
[argName: string]: mixed,
19+
},
20+
context: mixed,
21+
info: any,
22+
projection: { [fieldName: string]: true },
23+
[opt: string]: mixed,
24+
};
25+
26+
export type CursorDataType = {
527
[fieldName: string]: mixed,
628
};
729

830
export type connectionSortOpts = {
9-
resolver: string,
1031
uniqueFields: string[],
1132
sortValue: mixed,
12-
cursorToFilter: (<T>(cursorData: CursorData, filterArg: T) => T),
33+
cursorToFilter: (<T>(cursorData: CursorDataType, filterArg: T) => T),
1334
};
1435

1536
export type connectionSortMapOpts = {
1637
[sortName: string]: connectionSortOpts,
1738
};
1839

1940
export type composeWithConnectionOpts = {
41+
findResolverName: string,
42+
countResolverName: string,
2043
sort: connectionSortMapOpts,
2144
};
45+
46+
export type GraphQLConnectionType = {
47+
count: number,
48+
edges: [GraphQLConnectionEdgeType],
49+
pageInfo: PageInfoType,
50+
}
51+
52+
export type GraphQLConnectionEdgeType = {
53+
cursor: string,
54+
node: mixed,
55+
}
56+
57+
export type PageInfoType = {
58+
startCursor: string,
59+
endCursor: string,
60+
hasPreviousPage: boolean,
61+
hasNextPage: boolean,
62+
}

src/resolvers/connectionResolver.js

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
/* eslint-disable no-param-reassign, no-use-before-define */
33

44
import type {
5-
ExtendedResolveParams,
5+
ResolveParams,
6+
ConnectionResolveParams,
67
composeWithConnectionOpts,
78
connectionSortOpts,
9+
CursorDataType,
10+
GraphQLConnectionType,
811
} from '../definition';
912
import { Resolver, TypeComposer } from 'graphql-compose';
1013
import { GraphQLInt } from 'graphql';
@@ -25,15 +28,21 @@ export function prepareConnectionResolver(
2528
+ 'This function returns ID from provided object.');
2629
}
2730

28-
const countResolver = typeComposer.getResolver('count');
31+
const countResolver = typeComposer.getResolver(opts.countResolverName);
2932
if (!countResolver) {
30-
throw new Error(`TypeComposer(${typeComposer.getTypeName()}) should have 'count' resolver`);
33+
throw new Error(`TypeComposer(${typeComposer.getTypeName()}) provided to composeWithConnection `
34+
+ `should have resolver with name '${opts.countResolverName}' `
35+
+ 'due opts.countResolverName.');
3136
}
37+
const countResolve = countResolver.composeResolve();
3238

33-
const findManyResolver = typeComposer.getResolver('findMany');
39+
const findManyResolver = typeComposer.getResolver(opts.findResolverName);
3440
if (!findManyResolver) {
35-
throw new Error(`TypeComposer(${typeComposer.getTypeName()}) should have 'findMany' resolver`);
41+
throw new Error(`TypeComposer(${typeComposer.getTypeName()}) provided to composeWithConnection `
42+
+ `should have resolver with name '${opts.findResolverName}' `
43+
+ 'due opts.countResolverName.');
3644
}
45+
const findManyResolve = findManyResolver.composeResolve();
3746

3847
const additionalArgs = {};
3948
if (findManyResolver.hasArg('filter')) {
@@ -70,16 +79,17 @@ export function prepareConnectionResolver(
7079
description: 'Sort argument for data ordering',
7180
},
7281
},
73-
resolve: (resolveParams: ExtendedResolveParams) => {
82+
resolve: (resolveParams: ConnectionResolveParams) => {
7483
const { projection = {}, args = {} } = resolveParams;
75-
const findManyParams = Object.assign({}, resolveParams, {
76-
args: {},
77-
projection: {},
78-
});
79-
const connSortOpts: connectionSortOpts = resolveParams.args.sort;
84+
const findManyParams: ResolveParams = Object.assign(
85+
{},
86+
resolveParams,
87+
{ args: {} } // clear this params in copy
88+
);
89+
const sortOptions: connectionSortOpts = args.sort;
8090

81-
const first = args.first;
82-
const last = args.last;
91+
const first = parseInt(args.first, 10);
92+
const last = parseInt(args.last, 10);
8393

8494
const limit = last || first;
8595
const skip = (first - last) || 0;
@@ -89,30 +99,40 @@ export function prepareConnectionResolver(
8999
findManyParams.args.skip = skip;
90100
}
91101

92-
let filter = findManyParams.args.filter;
102+
let filter = findManyParams.args.filter || {};
93103
const beginCursorData = cursorToData(args.after);
94104
if (beginCursorData) {
95-
filter = connSortOpts.cursorToFilter(beginCursorData, filter);
105+
filter = sortOptions.cursorToFilter(beginCursorData, filter);
96106
}
97107
const endCursorData = cursorToData(args.before);
98108
if (endCursorData) {
99-
filter = connSortOpts.cursorToFilter(endCursorData, filter);
109+
filter = sortOptions.cursorToFilter(endCursorData, filter);
100110
}
101111
findManyParams.args.filter = filter;
102-
103112
findManyParams.args.skip = skip;
104-
// findManyParams.args.sort // TODO
105-
// findManyParams.projection // TODO
113+
114+
findManyParams.args.sort = sortOptions.sortValue;
115+
findManyParams.projection = projection;
116+
sortOptions.uniqueFields.forEach(fieldName => {
117+
findManyParams.projection[fieldName] = true;
118+
});
106119

107120
let countPromise;
108121
if (projection.count) {
109-
countPromise = countResolver(resolveParams);
122+
countPromise = countResolve(resolveParams);
110123
}
111-
const findManyPromise = findManyResolver(findManyParams);
112124
const hasPreviousPage = skip > 0;
113125
let hasNextPage = false; // will be requested +1 document, to check next page presence
114126

115-
return findManyPromise
127+
const filterDataForCursor = (record) => {
128+
const result = {};
129+
sortOptions.uniqueFields.forEach(fieldName => {
130+
result[fieldName] = record[fieldName];
131+
});
132+
return result;
133+
};
134+
135+
return findManyResolve(findManyParams)
116136
.then(recordList => {
117137
const edges = [];
118138
// if returned more than `limit` records, strip array and mark that exists next page
@@ -122,9 +142,8 @@ export function prepareConnectionResolver(
122142
}
123143
// transform record to object { cursor, node }
124144
recordList.forEach(record => {
125-
const id = typeComposer.getRecordId(record);
126145
edges.push({
127-
cursor: idToCursor(id),
146+
cursor: dataToCursor(filterDataForCursor(record)),
128147
node: record,
129148
});
130149
});
@@ -155,41 +174,30 @@ export function prepareConnectionResolver(
155174
});
156175
}
157176

158-
export function emptyConnection() {
177+
export function emptyConnection(): GraphQLConnectionType {
159178
return {
160179
count: 0,
161180
edges: [],
162181
pageInfo: {
163-
startCursor: null,
164-
endCursor: null,
182+
startCursor: '',
183+
endCursor: '',
165184
hasPreviousPage: false,
166185
hasNextPage: false,
167186
},
168187
};
169188
}
170189

171-
export function idToCursor(id: string) {
172-
return id;
173-
}
174-
175-
export function cursorToId(cursor: string) {
176-
return cursor;
177-
}
178-
179-
180-
181-
var PREFIX = 'arrayconnection:';
182-
183-
/**
184-
* Creates the cursor string from an offset.
185-
*/
186-
export function offsetToCursor(offset: number): ConnectionCursor {
187-
return base64(PREFIX + offset);
190+
export function cursorToData(id?: ?string): ?CursorDataType {
191+
if (id) {
192+
try {
193+
return JSON.parse(id) || null;
194+
} catch (err) {
195+
return null;
196+
}
197+
}
198+
return null;
188199
}
189200

190-
/**
191-
* Rederives the offset from the cursor string.
192-
*/
193-
export function cursorToOffset(cursor: ConnectionCursor): number {
194-
return parseInt(unbase64(cursor).substring(PREFIX.length), 10);
201+
export function dataToCursor(cursorData: CursorDataType): string {
202+
return JSON.stringify(cursorData);
195203
}

src/types/connectionType.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
GraphQLNonNull,
88
GraphQLList,
99
} from 'graphql';
10-
import GraphQLConnectionCursor from './сursorType';
10+
import GraphQLConnectionCursor from './cursorType';
1111
import type {
1212
TypeComposer,
1313
} from 'graphql-compose';

src/types/sortInputType.js

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
composeWithConnectionOpts,
88
connectionSortOpts,
99
} from '../definition.js';
10-
import { isFunction } from '../misc/is';
10+
import { isFunction } from '../utils/is';
1111

1212
export function prepareSortType(
1313
typeComposer: TypeComposer,
@@ -27,7 +27,7 @@ export function prepareSortType(
2727

2828
const sortEnumValues = {};
2929
sortKeys.forEach(sortKey => {
30-
checkSortOpts(sortKey, opts.sort[sortKey], typeComposer);
30+
checkSortOpts(sortKey, opts.sort[sortKey]);
3131

3232
sortEnumValues[sortKey] = {
3333
name: sortKey,
@@ -44,19 +44,12 @@ export function prepareSortType(
4444
}
4545

4646

47-
export function checkSortOpts(key: string, opts: connectionSortOpts, typeComposer: TypeComposer) {
47+
export function checkSortOpts(key: string, opts: connectionSortOpts) {
4848
if (!opts.resolver) {
4949
throw new Error('You should provide `resolver` option '
5050
+ `for composeWithConnection in opts.sort.${key}`);
5151
}
5252

53-
const resolver = typeComposer.getResolver(opts.resolver);
54-
if (!resolver) {
55-
throw new Error(`TypeComposer(${typeComposer.getTypeName()}) provided to composeWithConnection `
56-
+ `does not have resolver with name '${opts.resolver}' `
57-
+ `for opts.sort.${key}`);
58-
}
59-
6053
if (!opts.uniqueFields || !Array.isArray(opts.uniqueFields)) {
6154
throw new Error('You should array of field(s) in `uniqueFields` '
6255
+ `for composeWithConnection in opts.sort.${key}`

src/utils/deepmerge.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* @flow */
22
/* eslint-disable no-param-reassign */
33

4-
export default function deepmerge(target: Object | void, src: Object): Object | mixed[] {
4+
export default function deepmerge(target: Object, src: Object): Object | mixed[] {
55
if (Array.isArray(src)) {
66
let dst = [];
77
target = target || [];

0 commit comments

Comments
 (0)