Skip to content

Commit

Permalink
Merge pull request #167 from marmelab/fix-dates-introspection
Browse files Browse the repository at this point in the history
Fix: ISO string dates shouldn't be considered dates
  • Loading branch information
fzaninotto authored Apr 15, 2024
2 parents 4dc898a + bc074bc commit e221c70
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 15 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: "Test - action"

on:
push:
branches:
- master
pull_request:

jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js LTS
uses: actions/setup-node@v1
with:
node-version: '14.x'
- uses: bahmutov/npm-install@v1
with:
install-command: yarn --immutable
- name: Unit Tests
run: make test
env:
CI: true
4 changes: 3 additions & 1 deletion src/introspection/DateType.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ export const isISODateString = (value) => {
return d.toISOString() === value;
};

export const GraphQLDate = 'Date';

export default new GraphQLScalarType({
name: 'Date',
name: GraphQLDate,
description: 'Date type',
parseValue(value) {
// value comes from the client
Expand Down
3 changes: 2 additions & 1 deletion src/introspection/getFilterTypesFromData.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import getFieldsFromEntities from './getFieldsFromEntities';
import getValuesFromEntities from './getValuesFromEntities';
import getTypeFromValues from './getTypeFromValues';
import { getTypeFromKey } from '../nameConverter';
import { GraphQLDate } from './DateType';

const getRangeFiltersFromEntities = (entities) => {
const fieldValues = getValuesFromEntities(entities);
Expand All @@ -24,7 +25,7 @@ const getRangeFiltersFromEntities = (entities) => {
fieldType == GraphQLInt ||
fieldType == GraphQLFloat ||
fieldType.name == GraphQLString ||
fieldType.name == 'Date'
fieldType.name == GraphQLDate
) {
fields[`${fieldName}_lt`] = { type: fieldType };
fields[`${fieldName}_lte`] = { type: fieldType };
Expand Down
84 changes: 73 additions & 11 deletions src/resolver/Query/applyFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,71 @@ export default (entityData = [], filter = {}) => {
if (key.indexOf('_neq') !== -1) {
// not equal to
const realKey = key.replace(/(_neq)$/, '');
items = items.filter((d) => d[realKey] != filter[key]);
items = items.filter((d) => {
if (
filter[key] instanceof Date &&
typeof d[realKey] === 'string'
) {
return d[realKey] != filter[key].toISOString();
}
return d[realKey] != filter[key];
});
return;
}
if (key.indexOf('_lte') !== -1) {
// less than or equal
const realKey = key.replace(/(_lte)$/, '');
items = items.filter((d) => d[realKey] <= filter[key]);
items = items.filter((d) => {
if (
filter[key] instanceof Date &&
typeof d[realKey] === 'string'
) {
return d[realKey] <= filter[key].toISOString();
}
return d[realKey] <= filter[key];
});
return;
}
if (key.indexOf('_gte') !== -1) {
// less than or equal
const realKey = key.replace(/(_gte)$/, '');
items = items.filter((d) => d[realKey] >= filter[key]);
items = items.filter((d) => {
if (
filter[key] instanceof Date &&
typeof d[realKey] === 'string'
) {
return d[realKey] >= filter[key].toISOString();
}
return d[realKey] >= filter[key];
});
return;
}
if (key.indexOf('_lt') !== -1) {
// less than or equal
const realKey = key.replace(/(_lt)$/, '');
items = items.filter((d) => d[realKey] < filter[key]);
items = items.filter((d) => {
if (
filter[key] instanceof Date &&
typeof d[realKey] === 'string'
) {
return d[realKey] < filter[key].toISOString();
}
return d[realKey] < filter[key];
});
return;
}
if (key.indexOf('_gt') !== -1) {
// less than or equal
const realKey = key.replace(/(_gt)$/, '');
items = items.filter((d) => d[realKey] > filter[key]);
items = items.filter((d) => {
if (
filter[key] instanceof Date &&
typeof d[realKey] === 'string'
) {
return d[realKey] > filter[key].toISOString();
}
return d[realKey] > filter[key];
});
return;
}

Expand All @@ -43,20 +83,42 @@ export default (entityData = [], filter = {}) => {
if (Array.isArray(item[key])) {
// array filter and array item value: where all items in values
return filter[key].every((v) =>
item[key].some((itemValue) => itemValue == v)
item[key].some((itemValue) => {
if (
v instanceof Date &&
typeof itemValue === 'string'
) {
return itemValue == v.toISOString();
}
return itemValue == v;
})
);
}
// where item in values
return (
filter[key].filter((v) => v == item[key]).length > 0
filter[key].filter((v) => {
if (
v instanceof Date &&
typeof item[key] === 'string'
) {
return item[key] == v.toISOString();
}
return v == item[key];
}).length > 0
);
});
} else {
items = items.filter((d) =>
filter[key] instanceof Date
items = items.filter((d) => {
if (
filter[key] instanceof Date &&
typeof d[key] === 'string'
) {
return d[key] == filter[key].toISOString();
}
return filter[key] instanceof Date
? +d[key] == +filter[key]
: d[key] == filter[key]
);
: d[key] == filter[key];
});
}
});

Expand Down
93 changes: 93 additions & 0 deletions src/resolver/Query/applyFilters.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,96 @@ test('should filter by value if filter contains an array for the key', () => {
},
]);
});

test('should filter by date even if the data contains ISO string dates', () => {
const data = [
{
id: 1,
date: '2020-01-01T00:00:00.000Z',
},
{
id: 2,
date: '2020-01-02T00:00:00.000Z',
},
];

expect(
applyFilters(data, { date: new Date('2020-01-01T00:00:00.000Z') })
).toEqual([
{
id: 1,
date: '2020-01-01T00:00:00.000Z',
},
]);
expect(
applyFilters(data, { date_neq: new Date('2020-01-01T00:00:00.000Z') })
).toEqual([
{
id: 2,
date: '2020-01-02T00:00:00.000Z',
},
]);
expect(
applyFilters(data, { date_lt: new Date('2020-01-02T00:00:00.000Z') })
).toEqual([
{
id: 1,
date: '2020-01-01T00:00:00.000Z',
},
]);
expect(
applyFilters(data, { date_lte: new Date('2020-01-01:23:00.000Z') })
).toEqual([
{
id: 1,
date: '2020-01-01T00:00:00.000Z',
},
]);
expect(
applyFilters(data, { date_gt: new Date('2020-01-01T00:00:00.000Z') })
).toEqual([
{
id: 2,
date: '2020-01-02T00:00:00.000Z',
},
]);
expect(
applyFilters(data, { date_gte: new Date('2020-01-01T23:00:00.000Z') })
).toEqual([
{
id: 2,
date: '2020-01-02T00:00:00.000Z',
},
]);
expect(
applyFilters(data, { date: [new Date('2020-01-01T00:00:00.000Z')] })
).toEqual([
{
id: 1,
date: '2020-01-01T00:00:00.000Z',
},
]);
expect(
applyFilters(
[
{
id: 1,
dates: [
'2020-01-01T00:00:00.000Z',
'2020-01-02T00:00:00.000Z',
],
},
{
id: 2,
dates: ['2020-01-02T00:00:00.000Z'],
},
],
{ dates: [new Date('2020-01-01T00:00:00.000Z')] }
)
).toEqual([
{
id: 1,
dates: ['2020-01-01T00:00:00.000Z', '2020-01-02T00:00:00.000Z'],
},
]);
});
4 changes: 2 additions & 2 deletions src/resolver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import update from './Mutation/update';
import remove from './Mutation/remove';
import entityResolver from './Entity';
import { getTypeFromKey } from '../nameConverter';
import DateType from '../introspection/DateType';
import DateType, { GraphQLDate } from '../introspection/DateType';
import hasType from '../introspection/hasType';

const getQueryResolvers = (entityName, data) => ({
Expand Down Expand Up @@ -56,7 +56,7 @@ export default (data) => {
}),
{}
),
hasType('Date', data) ? { Date: DateType } : {}, // required because makeExecutableSchema strips resolvers from typeDefs
hasType(GraphQLDate, data) ? { Date: DateType } : {}, // required because makeExecutableSchema strips resolvers from typeDefs
hasType('JSON', data) ? { JSON: GraphQLJSON } : {} // required because makeExecutableSchema strips resolvers from typeDefs
);
};

0 comments on commit e221c70

Please sign in to comment.