Skip to content

Commit

Permalink
Merge pull request #248 from cofacts/contributions
Browse files Browse the repository at this point in the history
include contributions in GetUser query
  • Loading branch information
MrOrz authored Mar 3, 2021
2 parents bb67787 + f1a1cbb commit a78340a
Show file tree
Hide file tree
Showing 10 changed files with 374 additions and 1 deletion.
88 changes: 88 additions & 0 deletions src/graphql/dataLoaders/__fixtures__/contributionsLoaderFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export default {
'/replyrequests/doc/replyrequest1': {
userId: 'user1',
createdAt: '2019-01-01T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest2': {
userId: 'user1',
createdAt: '2020-02-01T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest3': {
userId: 'user2',
createdAt: '2020-02-01T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest4': {
userId: 'user1',
createdAt: '2020-02-02T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest5': {
userId: 'user1',
createdAt: '2020-02-03T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest6': {
userId: 'user1',
createdAt: '2020-02-03T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest7': {
userId: 'user1',
createdAt: '2020-02-04T00:00:00.000+08:00',
},

'/replies/doc/reply1': {
userId: 'user1',
createdAt: '2019-01-01T00:00:00.000+08:00',
},
'/replies/doc/reply2': {
userId: 'user2',
createdAt: '2020-01-01T00:00:00.000+08:00',
},
'/replies/doc/reply3': {
userId: 'user2',
createdAt: '2020-02-03T00:00:00.000+08:00',
},
'/replies/doc/reply4': {
userId: 'user1',
createdAt: '2020-02-01T00:00:00.000+08:00',
},
'/replies/doc/reply5': {
userId: 'user1',
createdAt: '2020-02-04T00:00:00.000+08:00',
},
'/replies/doc/reply6': {
userId: 'user1',
createdAt: '2020-02-09T00:00:00.000+08:00',
},
'/replies/doc/reply7': {
userId: 'user1',
createdAt: '2020-02-09T00:00:00.000+08:00',
},

'/articlereplyfeedbacks/doc/f1': {
userId: 'user1',
createdAt: '2019-01-01T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f2': {
userId: 'user2',
createdAt: '2020-01-01T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f3': {
userId: 'user2',
createdAt: '2020-02-03T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f4': {
userId: 'user1',
createdAt: '2020-02-01T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f5': {
userId: 'user1',
createdAt: '2020-02-04T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f6': {
userId: 'user1',
createdAt: '2020-02-06T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f7': {
userId: 'user1',
createdAt: '2020-02-08T00:00:00.000+08:00',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`contributionsLoaderFactory should load data for the date range specified 1`] = `
Array [
Object {
"count": 1,
"date": "2020-02-02",
},
Object {
"count": 2,
"date": "2020-02-03",
},
Object {
"count": 3,
"date": "2020-02-04",
},
Object {
"count": 1,
"date": "2020-02-06",
},
Object {
"count": 1,
"date": "2020-02-08",
},
Object {
"count": 2,
"date": "2020-02-09",
},
]
`;

exports[`contributionsLoaderFactory should load last year of data for given userId 1`] = `
Array [
Object {
"count": 3,
"date": "2020-02-01",
},
Object {
"count": 1,
"date": "2020-02-02",
},
Object {
"count": 2,
"date": "2020-02-03",
},
Object {
"count": 3,
"date": "2020-02-04",
},
Object {
"count": 1,
"date": "2020-02-06",
},
Object {
"count": 1,
"date": "2020-02-08",
},
Object {
"count": 2,
"date": "2020-02-09",
},
]
`;
42 changes: 42 additions & 0 deletions src/graphql/dataLoaders/__tests__/contirubtionsLoaderFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { loadFixtures, unloadFixtures } from 'util/fixtures';
import DataLoaders from '../index';
import fixtures from '../__fixtures__/contributionsLoaderFactory';
import MockDate from 'mockdate';

const loader = new DataLoaders();
MockDate.set(1609430400000); // 2021-01-01T00:00:00.000+08:00

describe('contributionsLoaderFactory', () => {
beforeAll(() => loadFixtures(fixtures));

it('should load last year of data for given userId', async () => {
const res = await loader.contributionsLoader.load({
userId: 'user1',
});
expect(res).toMatchSnapshot();
});

it('should load data for the date range specified', async () => {
expect(
await loader.contributionsLoader.load({
userId: 'user1',
dateRange: {
gte: '2020-02-01T00:00:00.000Z',
lte: '2020-03-01T00:00:00.000Z',
},
})
).toMatchSnapshot();
});

it('should throw error if userId is not present', async () => {
let error;
try {
await loader.contributionsLoader.load({});
} catch (e) {
error = e.message;
}
expect(error).toBe('userId is required');
});

afterAll(() => unloadFixtures(fixtures));
});
76 changes: 76 additions & 0 deletions src/graphql/dataLoaders/contributionsLoaderFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { subDays, startOfWeek } from 'date-fns';
import DataLoader from 'dataloader';
import client from 'util/client';
import { getRangeFieldParamFromArithmeticExpression } from 'graphql/util';

const defaultDuration = 365;

export default () =>
new DataLoader(
async statsQueries => {
const body = [];
const defaultEndDate = new Date();
const defaultStartDate = startOfWeek(
subDays(defaultEndDate, defaultDuration)
);
const defaultDateRange = {
gt: defaultStartDate,
lte: defaultEndDate,
};
statsQueries.forEach(({ userId, dateRange = defaultDateRange }) => {
if (!userId) throw new Error('userId is required');
body.push({
index: ['replyrequests', 'replies', 'articlereplyfeedbacks'],
type: 'doc',
});
body.push({
query: {
bool: {
must: [
{ term: { userId } },
{
range: {
createdAt: getRangeFieldParamFromArithmeticExpression(
dateRange
),
},
},
],
},
},
size: 0,
aggs: {
contributions: {
date_histogram: {
field: 'createdAt',
interval: 'day',
min_doc_count: 1,
format: 'yyyy-MM-dd',
time_zone: '+08:00',
},
},
},
});
});

return (await client.msearch({
body,
})).body.responses.map(
({
aggregations: {
contributions: { buckets },
},
}) =>
buckets
? buckets.map(bucket => ({
date: bucket.key_as_string,
count: bucket.doc_count,
}))
: []
);
},
{
cacheKeyFn: ({ userId, dateRange }) =>
`${userId}/${JSON.stringify(dateRange)}`,
}
);
8 changes: 8 additions & 0 deletions src/graphql/dataLoaders/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import repliedArticleCountLoaderFactory from './repliedArticleCountLoaderFactory
import votedArticleReplyCountLoaderFactory from './votedArticleReplyCountLoaderFactory';
import userLevelLoaderFactory from './userLevelLoaderFactory';
import userLoaderFactory from './userLoaderFactory';
import contributionsLoaderFactory from './contributionsLoaderFactory';

export default class DataLoaders {
// List of data loaders
Expand Down Expand Up @@ -78,6 +79,13 @@ export default class DataLoaders {
return this._checkOrSetLoader('analyticsLoader', analyticsLoaderFactory);
}

get contributionsLoader() {
return this._checkOrSetLoader(
'contributionsLoader',
contributionsLoaderFactory
);
}

// inner-workings
//
constructor() {
Expand Down
9 changes: 9 additions & 0 deletions src/graphql/models/Contribution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql';

export default new GraphQLObjectType({
name: 'Contribution',
fields: () => ({
date: { type: GraphQLString },
count: { type: GraphQLInt },
}),
});
20 changes: 20 additions & 0 deletions src/graphql/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
avatarUrlResolver,
} from 'util/user';
import AvatarTypeEnum from './AvatarTypeEnum';
import Contribution from './Contribution';
import { timeRangeInput } from 'graphql/util';

/**
* Field config helper for current user only field.
Expand Down Expand Up @@ -129,6 +131,24 @@ const User = new GraphQLObjectType({
createdAt: { type: GraphQLString },
updatedAt: { type: GraphQLString },
lastActiveAt: { type: GraphQLString },

contributions: {
type: new GraphQLList(Contribution),
description: 'List of contributions made by the user',
args: {
dateRange: {
type: timeRangeInput,
description:
'List only the contributions between the specific time range.',
},
},
resolve: async ({ id }, { dateRange }, { loaders }) => {
return await loaders.contributionsLoader.load({
userId: id,
dateRange,
});
},
},
}),
});

Expand Down
27 changes: 27 additions & 0 deletions src/graphql/queries/__fixtures__/GetUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,31 @@ export default {
createdAt: '2020-03-06T00:00:00.000Z',
updatedAt: '2020-04-06T00:00:00.000Z',
},

'/replyrequests/doc/replyrequest1': {
userId: 'current-user',
createdAt: '2020-02-01T00:00:00.000+08:00',
},
'/replyrequests/doc/replyrequest2': {
userId: 'current-user',
createdAt: '2020-02-02T00:00:00.000+08:00',
},

'/replies/doc/reply1': {
userId: 'current-user',
createdAt: '2020-02-02T00:00:00.000+08:00',
},
'/replies/doc/reply2': {
userId: 'current-user',
createdAt: '2020-02-04T00:00:00.000+08:00',
},

'/articlereplyfeedbacks/doc/f2': {
userId: 'current-user',
createdAt: '2020-02-03T00:00:00.000+08:00',
},
'/articlereplyfeedbacks/doc/f3': {
userId: 'current-user',
createdAt: '2020-02-01T00:00:00.000+08:00',
},
};
16 changes: 16 additions & 0 deletions src/graphql/queries/__tests__/GetUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,21 @@ describe('GetUser', () => {
).toMatchSnapshot('openPeepsUser');
});

it('returns contributions of a user', async () => {
expect(
await gql`
{
GetUser {
id
contributions(dateRange: { GTE: "2020-02-01", LTE: "2020-02-05" }) {
date
count
}
}
}
`({}, { user: currentUser })
).toMatchSnapshot();
});

afterAll(() => unloadFixtures(fixtures));
});
Loading

0 comments on commit a78340a

Please sign in to comment.