Skip to content

Commit

Permalink
replace order by pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-adams committed Sep 18, 2024
1 parent 568ae61 commit d99cfbc
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 67 deletions.
7 changes: 1 addition & 6 deletions packages/client/src/components/GrantActivity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,7 @@ export default {
async fetchFollowState() {
const followCalls = [
this.getFollowerForGrant({ grantId: this.currentGrant.grant_id }),
this.getFollowersForGrant({
grantId: this.currentGrant.grant_id,
orderBy: 'created_at',
orderDir: 'desc',
limit: 51,
}),
this.getFollowersForGrant({ grantId: this.currentGrant.grant_id, limit: 51 }),
];
const [userFollowsResult, followersResult] = await Promise.all(followCalls);
Expand Down
12 changes: 6 additions & 6 deletions packages/client/src/components/Modals/GrantFollowers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const mockStore = {

const store = createStore(mockStore);

const getMockFollowers = (count, hasMore = true) => ({
const getMockFollowers = (count, hasMoreCursor = null) => ({
followers: Array.from(Array(count), () => ({
id: Math.random(),
user: {
Expand All @@ -31,7 +31,7 @@ const getMockFollowers = (count, hasMore = true) => ({
},
})),
pagination: {
next: hasMore ? count : null,
next: hasMoreCursor,
},
});

Expand Down Expand Up @@ -64,23 +64,23 @@ describe('GrantFollowers.vue', () => {

it('should load re-fetch followers when user clicks Show More', async () => {
// Mock first batch of records
mockStore.actions['grants/getFollowersForGrant'].mockResolvedValue(getMockFollowers(20, true));
mockStore.actions['grants/getFollowersForGrant'].mockResolvedValue(getMockFollowers(20, 'id-x'));

const modal = wrapper.findComponent({ name: 'b-modal' });
modal.trigger('show');

await flushPromises();
expect(mockStore.actions['grants/getFollowersForGrant'].mock.lastCall[1].offset).to.equal(0);
expect(mockStore.actions['grants/getFollowersForGrant'].mock.lastCall[1].paginateFrom).toBeUndefined();

const showMoreBtn = wrapper.findComponent('[data-test-show-more-btn]');

// Mock second batch of records
mockStore.actions['grants/getFollowersForGrant'].mockResolvedValue(getMockFollowers(20, false));
mockStore.actions['grants/getFollowersForGrant'].mockResolvedValue(getMockFollowers(20));

showMoreBtn.trigger('click');

await flushPromises();
expect(mockStore.actions['grants/getFollowersForGrant'].mock.lastCall[1].offset).to.equal(20);
expect(mockStore.actions['grants/getFollowersForGrant'].mock.lastCall[1].paginateFrom).to.equal('id-x');

const followers = wrapper.findAll('[data-test-follower]');

Expand Down
15 changes: 10 additions & 5 deletions packages/client/src/components/Modals/GrantFollowers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default {
data() {
return {
followersLoaded: false,
followersOffset: 0,
followersNextCursor: null,
loading: false,
loadMoreVisible: false,
followers: [],
Expand Down Expand Up @@ -164,14 +164,15 @@ export default {
const query = {
grantId: this.currentGrant.grant_id,
limit: 20,
offset: this.followersOffset,
orderBy: 'users.name',
orderDir: 'asc',
};
if (this.followersNextCursor !== null) {
query.paginateFrom = this.followersNextCursor;
}
const result = await this.getFollowersForGrant(query);
this.followers = this.followers.concat(result.followers);
this.followersOffset = result.pagination.next;
this.followersNextCursor = result.pagination.next;
this.followersLoaded = true;
// more to load?
Expand Down Expand Up @@ -199,4 +200,8 @@ export default {
.follower-date {
font-size: 0.75rem;
}
.follower-date:first-letter {
text-transform: capitalize;
}
</style>
8 changes: 2 additions & 6 deletions packages/client/src/store/modules/grants.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,8 @@ export default {
return null;
}
},
async getFollowersForGrant({ rootGetters, commit }, {
grantId, offset, limit, orderBy, orderDir,
}) {
const queryParams = serializeQuery({
offset, limit, orderBy, orderDir,
});
async getFollowersForGrant({ rootGetters, commit }, { grantId, limit, paginateFrom }) {
const queryParams = serializeQuery({ limit, paginateFrom });
try {
return await fetchApi.get(`/api/organizations/${rootGetters['users/selectedAgencyId']}/grants/${grantId}/followers${queryParams}`);
} catch (e) {
Expand Down
19 changes: 3 additions & 16 deletions packages/server/__tests__/api/grants.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -988,9 +988,6 @@ HHS-2021-IHS-TPI-0001,Community Health Aide Program: Tribal Planning &`;
context('GET /:grantId/followers', () => {
const GRANT_ID = 335255;

const ORDER_BY = 'created_at';
const ORDER_DIR = 'desc';

let follower1;
let follower2;
beforeEach(async () => {
Expand All @@ -1002,30 +999,20 @@ HHS-2021-IHS-TPI-0001,Community Health Aide Program: Tribal Planning &`;
});

it('retrieves followers for a grant', async () => {
const response = await fetchApi(`/grants/${GRANT_ID}/followers`, agencies.own, fetchOptions.admin, {
orderBy: ORDER_BY,
orderDir: ORDER_DIR,
});
const response = await fetchApi(`/grants/${GRANT_ID}/followers`, agencies.own, fetchOptions.admin);
const respBody = await response.json();

expect(respBody.followers).to.have.lengthOf(2);
expect(respBody.followers[0].id).to.equal(follower2.id);
});
it('retrieves followers for a grant with LIMIT', async () => {
const response = await fetchApi(`/grants/${GRANT_ID}/followers`, agencies.own, fetchOptions.admin, {
orderBy: ORDER_BY,
orderDir: ORDER_DIR,
limit: 1,
});
const response = await fetchApi(`/grants/${GRANT_ID}/followers?limit=1`, agencies.own, fetchOptions.admin);
const respBody = await response.json();

expect(respBody.followers).to.have.lengthOf(1);
});
it('retrieves followers for a grant with PAGINATION', async () => {
const response = await fetchApi(`/grants/${GRANT_ID}/followers?offset=1`, agencies.own, fetchOptions.admin, {
orderBy: ORDER_BY,
orderDir: ORDER_DIR,
});
const response = await fetchApi(`/grants/${GRANT_ID}/followers?paginateFrom=${follower2.id}`, agencies.own, fetchOptions.admin);
const respBody = await response.json();

expect(respBody.followers).to.have.lengthOf(1);
Expand Down
15 changes: 4 additions & 11 deletions packages/server/__tests__/lib/grants-collaboration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,6 @@ describe('Grants Collaboration', () => {
});
});
context('getFollowersForGrant', () => {
const ORDER_BY = 'created_at';
const ORDER_DIR = 'desc';

let follower1;
let follower2;
beforeEach(async () => {
Expand All @@ -199,19 +196,16 @@ describe('Grants Collaboration', () => {

it('retrieves ALL followers for a grant', async () => {
const result = await getFollowersForGrant(knex, fixtures.grants.earFellowship.grant_id, fixtures.agencies.accountancy.tenant_id, {
orderBy: ORDER_BY,
orderDir: ORDER_DIR,
beforeFollow: null,
});

expect(result.followers).to.have.lengthOf(2);
expect(result.followers[0].id).to.equal(follower2.id);
});

it('retrieves followers for a grant with OFFSET', async () => {
it('retrieves followers for a grant with PAGINATION', async () => {
const result = await getFollowersForGrant(knex, fixtures.grants.earFellowship.grant_id, fixtures.agencies.accountancy.tenant_id, {
orderBy: ORDER_BY,
orderDir: ORDER_DIR,
offset: 1,
beforeFollow: follower2.id,
});

expect(result.followers).to.have.lengthOf(1);
Expand All @@ -221,12 +215,11 @@ describe('Grants Collaboration', () => {

it('retrieves followers for a grant with LIMIT', async () => {
const result = await getFollowersForGrant(knex, fixtures.grants.earFellowship.grant_id, fixtures.agencies.accountancy.tenant_id, {
orderBy: ORDER_BY,
orderDir: ORDER_DIR,
limit: 1,
});

expect(result.followers).to.have.lengthOf(1);
expect(result.pagination.next).to.equal(follower2.id);
});
});
});
17 changes: 11 additions & 6 deletions packages/server/src/lib/grantsCollaboration/followers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async function getFollowerForGrant(knex, grantId, userId) {
}

async function getFollowersForGrant(knex, grantId, organizationId, {
offset = 0, limit = 50, orderBy = 'created_at', orderDir = 'desc',
beforeFollow, limit = 50,
} = {}) {
const query = knex('grant_followers')
.select(
Expand All @@ -65,18 +65,23 @@ async function getFollowersForGrant(knex, grantId, organizationId, {
.join('tenants', 'tenants.id', 'users.tenant_id')
.where('grant_followers.grant_id', grantId)
.andWhere('tenants.id', organizationId)
.orderBy(orderBy, orderDir)
.offset(offset)
.orderBy('created_at', 'desc')
.limit(limit + 1);

if (beforeFollow) {
query.andWhere('grant_followers.id', '<', beforeFollow);
}

const grantFollowersResult = await query;
const hasMore = grantFollowersResult.length > limit;

// remove forward looking extra
const grantFollowers = grantFollowersResult.length > limit
const grantFollowersReturn = hasMore
? grantFollowersResult.slice(0, -1)
: grantFollowersResult;

return {
followers: grantFollowers
followers: grantFollowersReturn
.map((grantFollower) => ({
id: grantFollower.id,
createdAt: grantFollower.created_at,
Expand All @@ -98,7 +103,7 @@ async function getFollowersForGrant(knex, grantId, organizationId, {
},
})),
pagination: {
next: grantFollowersResult.length > limit ? offset + limit : null,
next: hasMore ? grantFollowersReturn[grantFollowersReturn.length - 1].id : null,
},
};
}
Expand Down
14 changes: 3 additions & 11 deletions packages/server/src/routes/grants.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,25 +489,17 @@ router.put('/:grantId/notes/revision', requireUser, async (req, res) => {
router.get('/:grantId/followers', requireUser, async (req, res) => {
const { grantId } = req.params;
const { user } = req.session;
const {
offset, limit, orderBy, orderDir,
} = req.query;
const { paginateFrom, limit } = req.query;
const limitInt = limit ? parseInt(limit, 10) : undefined;
const offsetInt = offset ? parseInt(offset, 10) : undefined;

const invalidLimit = limit && (!Number.isInteger(limitInt) || limitInt < 1 || limitInt > 100);
const invalidOffset = offset && (!Number.isInteger(offsetInt) || offset < 0);

if (invalidLimit || invalidOffset) {
if (limit && (!Number.isInteger(limitInt) || limitInt < 1 || limitInt > 100)) {
res.sendStatus(400);
return;
}

const followers = await getFollowersForGrant(knex, grantId, user.tenant_id, {
offset: offsetInt,
beforeFollow: paginateFrom,
limit: limitInt,
orderBy,
orderDir,
});

res.json(followers);
Expand Down

0 comments on commit d99cfbc

Please sign in to comment.