Skip to content

Commit 926ceb1

Browse files
Scimonsternodkz
authored andcommitted
feat: Add option to specify connectionResolverName
1 parent d00e53f commit 926ceb1

9 files changed

+120
-10
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import composeWithConnection from 'graphql-compose-connection';
3636
import userTypeComposer from './user.js';
3737

3838
composeWithConnection(userTypeComposer, {
39+
connectionResolverName: 'connection', // Default
3940
findResolverName: 'findMany',
4041
countResolverName: 'count',
4142
sort: {

src/__tests__/composeWithConnection-test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,53 @@ describe('composeWithRelay', () => {
5050
expect(myTC.getResolver('connection')).toBeTruthy();
5151
expect(myTC.getResolver('connection').resolve()).toBe('mockData');
5252
});
53+
54+
it('should add resolver with user-specified name', () => {
55+
let myTC = TypeComposer.create('type CustomComplex { a: String, b: Int }');
56+
myTC.addResolver({
57+
name: 'count',
58+
resolve: () => 1,
59+
});
60+
myTC.addResolver({
61+
name: 'findMany',
62+
resolve: () => ['mockData'],
63+
});
64+
myTC = composeWithConnection(myTC, {
65+
connectionResolverName: 'customConnection',
66+
countResolverName: 'count',
67+
findResolverName: 'findMany',
68+
sort: sortOptions,
69+
});
70+
71+
expect(myTC.getResolver('customConnection')).toBeTruthy();
72+
expect(myTC.hasResolver('connection')).toBeFalsy();
73+
});
74+
75+
it('should add two connection resolvers', () => {
76+
let myTC = TypeComposer.create('type CustomComplex { a: String, b: Int }');
77+
myTC.addResolver({
78+
name: 'count',
79+
resolve: () => 1,
80+
});
81+
myTC.addResolver({
82+
name: 'findMany',
83+
resolve: () => ['mockData'],
84+
});
85+
myTC = composeWithConnection(myTC, {
86+
countResolverName: 'count',
87+
findResolverName: 'findMany',
88+
sort: sortOptions,
89+
});
90+
myTC = composeWithConnection(myTC, {
91+
connectionResolverName: 'customConnection',
92+
countResolverName: 'count',
93+
findResolverName: 'findMany',
94+
sort: sortOptions,
95+
});
96+
97+
expect(myTC.hasResolver('connection')).toBeTruthy();
98+
expect(myTC.getResolver('customConnection')).toBeTruthy();
99+
});
53100
});
54101

55102
describe('check `connection` resolver props', () => {

src/composeWithConnection.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { TypeComposer } from 'graphql-compose';
44
import { prepareConnectionResolver } from './connectionResolver';
55
import type { ComposeWithConnectionOpts } from './connectionResolver';
6+
import { resolverName } from './utils/name';
67

78
export function composeWithConnection(
89
typeComposer: TypeComposer,
@@ -16,12 +17,12 @@ export function composeWithConnection(
1617
throw new Error('You should provide non-empty options to composeWithConnection');
1718
}
1819

19-
if (typeComposer.hasResolver('connection')) {
20+
if (typeComposer.hasResolver(resolverName(opts.connectionResolverName))) {
2021
return typeComposer;
2122
}
2223

2324
const resolver = prepareConnectionResolver(typeComposer, opts);
2425

25-
typeComposer.setResolver('connection', resolver);
26+
typeComposer.setResolver(resolverName(opts.connectionResolverName), resolver);
2627
return typeComposer;
2728
}

src/connectionResolver.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import type { GraphQLResolveInfo } from 'graphql-compose/lib/graphql';
1212
import { prepareConnectionType } from './types/connectionType';
1313
import { prepareSortType } from './types/sortInputType';
1414
import { cursorToData, dataToCursor, type CursorDataType } from './cursor';
15+
import { resolverName } from './utils/name';
1516

1617
export type ComposeWithConnectionOpts = {
18+
connectionResolverName?: string,
1719
findResolverName: string,
1820
countResolverName: string,
1921
sort: ConnectionSortMapOpts,
@@ -125,8 +127,8 @@ export function prepareConnectionResolver(
125127
const sortEnumType = prepareSortType(tc, opts);
126128

127129
return new tc.constructor.schemaComposer.Resolver({
128-
type: prepareConnectionType(tc),
129-
name: 'connection',
130+
type: prepareConnectionType(tc, opts.connectionResolverName),
131+
name: resolverName(opts.connectionResolverName),
130132
kind: 'query',
131133
args: {
132134
first: {

src/types/__tests__/connectionType-test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,31 @@ describe('types/connectionType.js', () => {
5555
expect(prepareConnectionType(userTypeComposer)).toBeInstanceOf(GraphQLObjectType);
5656
});
5757

58+
it('should return the same GraphQLObjectType object when called again', () => {
59+
const firstConnectionType = prepareConnectionType(userTypeComposer);
60+
const secondConnectionType = prepareConnectionType(userTypeComposer);
61+
expect(firstConnectionType).toBeInstanceOf(GraphQLObjectType);
62+
expect(firstConnectionType).toBe(secondConnectionType);
63+
});
64+
65+
it('should return a separate GraphQLObjectType with a different name', () => {
66+
const connectionType = prepareConnectionType(userTypeComposer);
67+
const otherConnectionType = prepareConnectionType(userTypeComposer, 'otherConnection');
68+
expect(connectionType).toBeInstanceOf(GraphQLObjectType);
69+
expect(otherConnectionType).toBeInstanceOf(GraphQLObjectType);
70+
expect(connectionType).not.toBe(otherConnectionType);
71+
});
72+
5873
it('should have name ending with `Connection`', () => {
5974
expect(prepareConnectionType(userTypeComposer).name).toBe('UserConnection');
6075
});
6176

77+
it('should have name ending with `OtherConnection` when passed lowercase otherConnection', () => {
78+
expect(prepareConnectionType(userTypeComposer, 'otherConnection').name).toBe(
79+
'UserOtherConnection'
80+
);
81+
});
82+
6283
it('should have field `count` with provided Type', () => {
6384
const tc = new TypeComposer(prepareConnectionType(userTypeComposer));
6485
const countType: any = tc.getFieldType('count');

src/types/__tests__/sortInputType-test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,23 @@ describe('types/sortInputType.js', () => {
141141
expect(sortType.name).toBe('SortConnectionUserEnum');
142142
});
143143

144+
it('should have name `Sort[resolverName][typeName]Enum`', () => {
145+
const otherSortType = prepareSortType(userTypeComposer, {
146+
sort: {
147+
_ID_ASC: {
148+
value: { id: 1 },
149+
cursorFields: ['id'],
150+
beforeCursorQuery: () => {},
151+
afterCursorQuery: () => {},
152+
},
153+
},
154+
findResolverName: 'finMany',
155+
countResolverName: 'count',
156+
connectionResolverName: 'otherConnection',
157+
});
158+
expect(otherSortType.name).toBe('SortOtherConnectionUserEnum');
159+
});
160+
144161
it('should have enum values', () => {
145162
const etc = EnumTypeComposer.create(sortType);
146163
expect(etc.hasField('_ID_ASC')).toBeTruthy();

src/types/connectionType.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import type { TypeComposer } from 'graphql-compose';
1212

1313
import PageInfoType from './pageInfoType';
14+
import { typeName } from '../utils/name';
1415

1516
const cachedConnectionTypes = new WeakMap();
1617
const cachedEdgeTypes = new WeakMap();
@@ -47,12 +48,15 @@ export function prepareEdgeType(typeComposer: TypeComposer): GraphQLObjectType {
4748
return edgeType;
4849
}
4950

50-
export function prepareConnectionType(typeComposer: TypeComposer): GraphQLObjectType {
51-
const name = `${typeComposer.getTypeName()}Connection`;
51+
export function prepareConnectionType(
52+
typeComposer: TypeComposer,
53+
resolverName: ?string
54+
): GraphQLObjectType {
55+
const name = `${typeComposer.getTypeName()}${typeName(resolverName)}`;
5256
const type = typeComposer.getType();
5357

54-
if (cachedConnectionTypes.has(type)) {
55-
return (cachedConnectionTypes.get(type): any);
58+
if (cachedConnectionTypes.has(type) && (cachedConnectionTypes.get(type): any).has(name)) {
59+
return ((cachedConnectionTypes.get(type): any).get(name): any);
5660
}
5761

5862
const connectionType = new GraphQLObjectType({
@@ -81,6 +85,10 @@ export function prepareConnectionType(typeComposer: TypeComposer): GraphQLObject
8185
// $FlowFixMe
8286
connectionType.ofType = type;
8387

84-
cachedConnectionTypes.set(type, connectionType);
88+
if (!cachedConnectionTypes.has(type)) {
89+
cachedConnectionTypes.set(type, new Map());
90+
// Can't use WeakMap because keys must be strings
91+
}
92+
(cachedConnectionTypes.get(type): any).set(name, connectionType);
8593
return connectionType;
8694
}

src/types/sortInputType.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { TypeComposer } from 'graphql-compose';
55
import { GraphQLEnumType } from 'graphql-compose/lib/graphql';
66
import { isFunction } from '../utils/is';
7+
import { typeName as uppercaseTypeName } from '../utils/name';
78
import type { ConnectionSortOpts, ComposeWithConnectionOpts } from '../connectionResolver';
89

910
export function prepareSortType(
@@ -14,7 +15,9 @@ export function prepareSortType(
1415
throw new Error('Option `sort` should not be empty in composeWithConnection');
1516
}
1617

17-
const typeName = `SortConnection${typeComposer.getTypeName()}Enum`;
18+
const typeName = `Sort${uppercaseTypeName(
19+
opts.connectionResolverName
20+
)}${typeComposer.getTypeName()}Enum`;
1821

1922
const sortKeys = Object.keys(opts.sort);
2023
if (sortKeys.length === 0) {

src/utils/name.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* @flow */
2+
3+
export function resolverName(name: ?string) {
4+
return name || 'connection';
5+
}
6+
7+
export function typeName(name: ?string) {
8+
const lowercaseName = resolverName(name);
9+
return lowercaseName[0].toUpperCase() + lowercaseName.slice(1);
10+
}

0 commit comments

Comments
 (0)