Skip to content

Commit e221c70

Browse files
authored
Merge pull request #167 from marmelab/fix-dates-introspection
Fix: ISO string dates shouldn't be considered dates
2 parents 4dc898a + bc074bc commit e221c70

File tree

6 files changed

+198
-15
lines changed

6 files changed

+198
-15
lines changed

.github/workflows/test.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: "Test - action"
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
jobs:
10+
unit-test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v2
15+
- name: Use Node.js LTS
16+
uses: actions/setup-node@v1
17+
with:
18+
node-version: '14.x'
19+
- uses: bahmutov/npm-install@v1
20+
with:
21+
install-command: yarn --immutable
22+
- name: Unit Tests
23+
run: make test
24+
env:
25+
CI: true

src/introspection/DateType.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ export const isISODateString = (value) => {
1010
return d.toISOString() === value;
1111
};
1212

13+
export const GraphQLDate = 'Date';
14+
1315
export default new GraphQLScalarType({
14-
name: 'Date',
16+
name: GraphQLDate,
1517
description: 'Date type',
1618
parseValue(value) {
1719
// value comes from the client

src/introspection/getFilterTypesFromData.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import getFieldsFromEntities from './getFieldsFromEntities';
1111
import getValuesFromEntities from './getValuesFromEntities';
1212
import getTypeFromValues from './getTypeFromValues';
1313
import { getTypeFromKey } from '../nameConverter';
14+
import { GraphQLDate } from './DateType';
1415

1516
const getRangeFiltersFromEntities = (entities) => {
1617
const fieldValues = getValuesFromEntities(entities);
@@ -24,7 +25,7 @@ const getRangeFiltersFromEntities = (entities) => {
2425
fieldType == GraphQLInt ||
2526
fieldType == GraphQLFloat ||
2627
fieldType.name == GraphQLString ||
27-
fieldType.name == 'Date'
28+
fieldType.name == GraphQLDate
2829
) {
2930
fields[`${fieldName}_lt`] = { type: fieldType };
3031
fields[`${fieldName}_lte`] = { type: fieldType };

src/resolver/Query/applyFilters.js

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,71 @@ export default (entityData = [], filter = {}) => {
1010
if (key.indexOf('_neq') !== -1) {
1111
// not equal to
1212
const realKey = key.replace(/(_neq)$/, '');
13-
items = items.filter((d) => d[realKey] != filter[key]);
13+
items = items.filter((d) => {
14+
if (
15+
filter[key] instanceof Date &&
16+
typeof d[realKey] === 'string'
17+
) {
18+
return d[realKey] != filter[key].toISOString();
19+
}
20+
return d[realKey] != filter[key];
21+
});
1422
return;
1523
}
1624
if (key.indexOf('_lte') !== -1) {
1725
// less than or equal
1826
const realKey = key.replace(/(_lte)$/, '');
19-
items = items.filter((d) => d[realKey] <= filter[key]);
27+
items = items.filter((d) => {
28+
if (
29+
filter[key] instanceof Date &&
30+
typeof d[realKey] === 'string'
31+
) {
32+
return d[realKey] <= filter[key].toISOString();
33+
}
34+
return d[realKey] <= filter[key];
35+
});
2036
return;
2137
}
2238
if (key.indexOf('_gte') !== -1) {
2339
// less than or equal
2440
const realKey = key.replace(/(_gte)$/, '');
25-
items = items.filter((d) => d[realKey] >= filter[key]);
41+
items = items.filter((d) => {
42+
if (
43+
filter[key] instanceof Date &&
44+
typeof d[realKey] === 'string'
45+
) {
46+
return d[realKey] >= filter[key].toISOString();
47+
}
48+
return d[realKey] >= filter[key];
49+
});
2650
return;
2751
}
2852
if (key.indexOf('_lt') !== -1) {
2953
// less than or equal
3054
const realKey = key.replace(/(_lt)$/, '');
31-
items = items.filter((d) => d[realKey] < filter[key]);
55+
items = items.filter((d) => {
56+
if (
57+
filter[key] instanceof Date &&
58+
typeof d[realKey] === 'string'
59+
) {
60+
return d[realKey] < filter[key].toISOString();
61+
}
62+
return d[realKey] < filter[key];
63+
});
3264
return;
3365
}
3466
if (key.indexOf('_gt') !== -1) {
3567
// less than or equal
3668
const realKey = key.replace(/(_gt)$/, '');
37-
items = items.filter((d) => d[realKey] > filter[key]);
69+
items = items.filter((d) => {
70+
if (
71+
filter[key] instanceof Date &&
72+
typeof d[realKey] === 'string'
73+
) {
74+
return d[realKey] > filter[key].toISOString();
75+
}
76+
return d[realKey] > filter[key];
77+
});
3878
return;
3979
}
4080

@@ -43,20 +83,42 @@ export default (entityData = [], filter = {}) => {
4383
if (Array.isArray(item[key])) {
4484
// array filter and array item value: where all items in values
4585
return filter[key].every((v) =>
46-
item[key].some((itemValue) => itemValue == v)
86+
item[key].some((itemValue) => {
87+
if (
88+
v instanceof Date &&
89+
typeof itemValue === 'string'
90+
) {
91+
return itemValue == v.toISOString();
92+
}
93+
return itemValue == v;
94+
})
4795
);
4896
}
4997
// where item in values
5098
return (
51-
filter[key].filter((v) => v == item[key]).length > 0
99+
filter[key].filter((v) => {
100+
if (
101+
v instanceof Date &&
102+
typeof item[key] === 'string'
103+
) {
104+
return item[key] == v.toISOString();
105+
}
106+
return v == item[key];
107+
}).length > 0
52108
);
53109
});
54110
} else {
55-
items = items.filter((d) =>
56-
filter[key] instanceof Date
111+
items = items.filter((d) => {
112+
if (
113+
filter[key] instanceof Date &&
114+
typeof d[key] === 'string'
115+
) {
116+
return d[key] == filter[key].toISOString();
117+
}
118+
return filter[key] instanceof Date
57119
? +d[key] == +filter[key]
58-
: d[key] == filter[key]
59-
);
120+
: d[key] == filter[key];
121+
});
60122
}
61123
});
62124

src/resolver/Query/applyFilters.spec.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,96 @@ test('should filter by value if filter contains an array for the key', () => {
193193
},
194194
]);
195195
});
196+
197+
test('should filter by date even if the data contains ISO string dates', () => {
198+
const data = [
199+
{
200+
id: 1,
201+
date: '2020-01-01T00:00:00.000Z',
202+
},
203+
{
204+
id: 2,
205+
date: '2020-01-02T00:00:00.000Z',
206+
},
207+
];
208+
209+
expect(
210+
applyFilters(data, { date: new Date('2020-01-01T00:00:00.000Z') })
211+
).toEqual([
212+
{
213+
id: 1,
214+
date: '2020-01-01T00:00:00.000Z',
215+
},
216+
]);
217+
expect(
218+
applyFilters(data, { date_neq: new Date('2020-01-01T00:00:00.000Z') })
219+
).toEqual([
220+
{
221+
id: 2,
222+
date: '2020-01-02T00:00:00.000Z',
223+
},
224+
]);
225+
expect(
226+
applyFilters(data, { date_lt: new Date('2020-01-02T00:00:00.000Z') })
227+
).toEqual([
228+
{
229+
id: 1,
230+
date: '2020-01-01T00:00:00.000Z',
231+
},
232+
]);
233+
expect(
234+
applyFilters(data, { date_lte: new Date('2020-01-01:23:00.000Z') })
235+
).toEqual([
236+
{
237+
id: 1,
238+
date: '2020-01-01T00:00:00.000Z',
239+
},
240+
]);
241+
expect(
242+
applyFilters(data, { date_gt: new Date('2020-01-01T00:00:00.000Z') })
243+
).toEqual([
244+
{
245+
id: 2,
246+
date: '2020-01-02T00:00:00.000Z',
247+
},
248+
]);
249+
expect(
250+
applyFilters(data, { date_gte: new Date('2020-01-01T23:00:00.000Z') })
251+
).toEqual([
252+
{
253+
id: 2,
254+
date: '2020-01-02T00:00:00.000Z',
255+
},
256+
]);
257+
expect(
258+
applyFilters(data, { date: [new Date('2020-01-01T00:00:00.000Z')] })
259+
).toEqual([
260+
{
261+
id: 1,
262+
date: '2020-01-01T00:00:00.000Z',
263+
},
264+
]);
265+
expect(
266+
applyFilters(
267+
[
268+
{
269+
id: 1,
270+
dates: [
271+
'2020-01-01T00:00:00.000Z',
272+
'2020-01-02T00:00:00.000Z',
273+
],
274+
},
275+
{
276+
id: 2,
277+
dates: ['2020-01-02T00:00:00.000Z'],
278+
},
279+
],
280+
{ dates: [new Date('2020-01-01T00:00:00.000Z')] }
281+
)
282+
).toEqual([
283+
{
284+
id: 1,
285+
dates: ['2020-01-01T00:00:00.000Z', '2020-01-02T00:00:00.000Z'],
286+
},
287+
]);
288+
});

src/resolver/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import update from './Mutation/update';
1010
import remove from './Mutation/remove';
1111
import entityResolver from './Entity';
1212
import { getTypeFromKey } from '../nameConverter';
13-
import DateType from '../introspection/DateType';
13+
import DateType, { GraphQLDate } from '../introspection/DateType';
1414
import hasType from '../introspection/hasType';
1515

1616
const getQueryResolvers = (entityName, data) => ({
@@ -56,7 +56,7 @@ export default (data) => {
5656
}),
5757
{}
5858
),
59-
hasType('Date', data) ? { Date: DateType } : {}, // required because makeExecutableSchema strips resolvers from typeDefs
59+
hasType(GraphQLDate, data) ? { Date: DateType } : {}, // required because makeExecutableSchema strips resolvers from typeDefs
6060
hasType('JSON', data) ? { JSON: GraphQLJSON } : {} // required because makeExecutableSchema strips resolvers from typeDefs
6161
);
6262
};

0 commit comments

Comments
 (0)