Skip to content

Commit e3ac7de

Browse files
sushantdhimanmickhansen
authored andcommitted
Order By with args (#339)
* orderBy now accepts args * test for orderby case * signature for orderBy func * no reversing of nulls * [ci skip] rename test * docs and test simplification * test desc change
1 parent 6f30aee commit e3ac7de

File tree

3 files changed

+85
-15
lines changed

3 files changed

+85
-15
lines changed

docs/relay.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ const userTaskConnection = sequelizeConnection({
101101
name: 'UserTaskOrderBy',
102102
values: {
103103
AGE: {value: ['createdAt', 'DESC']}, // The first ENUM value will be the default order. The direction will be used for `first`, will automatically be inversed for `last` lookups.
104-
TITLE: {value: ['title', 'ASC']}
104+
TITLE: {value: ['title', 'ASC']},
105+
CUSTOM: {value: [function (source, args, context, info) {}, 'ASC']} // build and return custom order for sequelize orderBy option
105106
}
106107
}),
107108
where: function (key, value) {

src/relay.js

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,17 @@ export function sequelizeConnection({
144144
}
145145
};
146146

147-
let orderByAttribute = function (orderBy) {
148-
return orderBy[0][0];
147+
let orderByAttribute = function (orderAttr, {source, args, context, info}) {
148+
return typeof orderAttr === 'function' ? orderAttr(source, args, context, info) : orderAttr;
149+
};
150+
151+
let orderByDirection = function (orderDirection, args) {
152+
if (args.last) {
153+
return orderDirection.indexOf('ASC') >= 0
154+
? orderDirection.replace('ASC', 'DESC')
155+
: orderDirection.replace('DESC', 'ASC');
156+
}
157+
return orderDirection;
149158
};
150159

151160
/**
@@ -211,22 +220,25 @@ export function sequelizeConnection({
211220
}
212221

213222
let orderBy = args.orderBy;
214-
let orderAttribute = orderByAttribute(orderBy);
215-
let orderDirection = args.orderBy[0][1];
216-
217-
if (args.last) {
218-
orderDirection = orderDirection === 'ASC' ? 'DESC' : 'ASC';
219-
}
223+
let orderAttribute = orderByAttribute(orderBy[0][0], {
224+
source: info.source,
225+
args,
226+
context,
227+
info
228+
});
229+
let orderDirection = orderByDirection(orderBy[0][1], args);
220230

221231
options.order = [
222232
[orderAttribute, orderDirection]
223233
];
224234

225235
if (orderAttribute !== model.primaryKeyAttribute) {
226-
options.order.push([model.primaryKeyAttribute, 'ASC']);
236+
options.order.push([model.primaryKeyAttribute, orderByDirection('ASC', args)]);
227237
}
228238

229-
options.attributes.push(orderAttribute);
239+
if (typeof orderAttribute === 'string') {
240+
options.attributes.push(orderAttribute);
241+
}
230242

231243
if (options.limit && !options.attributes.some(attribute => attribute.length === 2 && attribute[1] === 'full_count')) {
232244
if (model.sequelize.dialect.name === 'postgres') {

test/integration/relay/connection.test.js

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('relay', function () {
6464
}
6565
});
6666

67+
this.projectOrderSpy = sinon.spy(() => 'name');
6768
this.projectTaskConnectionFieldSpy = sinon.spy();
6869
this.projectTaskConnection = sequelizeConnection({
6970
name: 'projectTask',
@@ -74,7 +75,9 @@ describe('relay', function () {
7475
values: {
7576
ID: {value: [this.Task.primaryKeyAttribute, 'ASC']},
7677
LATEST: {value: ['createdAt', 'DESC']},
77-
NAME: {value: ['name', 'ASC']}
78+
NAME: {value: ['name', 'ASC']},
79+
NAME_FUNC: {value: [this.projectOrderSpy, 'ASC']},
80+
NAME_NULLS_LAST: {value: ['name', 'ASC NULLS LAST']}
7881
}
7982
}),
8083
connectionFields: () => ({
@@ -479,6 +482,60 @@ describe('relay', function () {
479482

480483
});
481484

485+
it('should handle orderBy function case', async function () {
486+
await graphql(this.schema, `
487+
{
488+
user(id: ${this.userA.id}) {
489+
projects(first: 1) {
490+
edges {
491+
node {
492+
tasks(orderBy: NAME_FUNC, first: 5) {
493+
edges {
494+
cursor
495+
node {
496+
id
497+
name
498+
}
499+
}
500+
}
501+
}
502+
}
503+
}
504+
}
505+
}
506+
`, null, {});
507+
508+
expect(this.projectOrderSpy).to.have.been.calledOnce;
509+
expect(this.projectOrderSpy.alwaysCalledWithMatch({}, { first: 5 })).to.be.ok;
510+
});
511+
512+
it('should properly reverse orderBy with NULLS and last', async function () {
513+
let sqlSpy = sinon.spy();
514+
await graphql(this.schema, `
515+
{
516+
user(id: ${this.userA.id}) {
517+
projects(first: 1) {
518+
edges {
519+
node {
520+
tasks(orderBy: NAME_NULLS_LAST, last: 10) {
521+
edges {
522+
cursor
523+
node {
524+
id
525+
name
526+
}
527+
}
528+
}
529+
}
530+
}
531+
}
532+
}
533+
}
534+
`, null, { logging: sqlSpy });
535+
536+
expect(sqlSpy.lastCall.args[0].match('DESC NULLS LAST')).to.be.ok;
537+
});
538+
482539
it('should support in-query slicing and pagination with first and orderBy', async function () {
483540
let firstThree = this.userA.tasks.slice(this.userA.tasks.length - 3, this.userA.tasks.length);
484541
let nextThree = this.userA.tasks.slice(this.userA.tasks.length - 6, this.userA.tasks.length - 3);
@@ -641,7 +698,7 @@ describe('relay', function () {
641698
});
642699

643700
it('should support pagination with where', async function () {
644-
const completedTasks = this.userA.tasks.filter(task => task.completed)
701+
const completedTasks = this.userA.tasks.filter(task => task.completed);
645702

646703
expect(completedTasks.length).to.equal(4);
647704

@@ -934,8 +991,8 @@ describe('relay', function () {
934991

935992
const nodeNames = result.data.user.projects.edges.map(edge => {
936993
return edge.node.tasks.edges.map(edge => {
937-
return edge.node.name
938-
}).sort()
994+
return edge.node.name;
995+
}).sort();
939996
});
940997
expect(nodeNames).to.deep.equal([
941998
[

0 commit comments

Comments
 (0)