From 309ba33c591ab227359b58cfbf67310d62cd799c Mon Sep 17 00:00:00 2001 From: Greg Adams <41639787+greg-adams@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:58:37 -0700 Subject: [PATCH] feat(grants-collaboration): expose notes retrieval (#3395) * add route for notes * add tests * remove use of db module * linting --- packages/server/__tests__/api/grants.test.js | 60 +++++++++++++++++++- packages/server/seeds/dev/01_main.js | 2 +- packages/server/src/db/index.js | 2 - packages/server/src/routes/grants.js | 18 +++++- 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/packages/server/__tests__/api/grants.test.js b/packages/server/__tests__/api/grants.test.js index d200e1723..76eb95235 100644 --- a/packages/server/__tests__/api/grants.test.js +++ b/packages/server/__tests__/api/grants.test.js @@ -4,6 +4,8 @@ const { getSessionCookie, makeTestServer, knex } = require('./utils'); const { TABLES } = require('../../src/db/constants'); const db = require('../../src/db'); const email = require('../../src/lib/email'); +const users = require('../../seeds/dev/ref/users'); +const { seed } = require('../../seeds/dev/01_main'); /* In general, these tests ... @@ -20,6 +22,9 @@ describe('`/api/grants` endpoint', () => { dallasAdmin: 386, }; + const adminUser = users.find((usr) => usr.email === 'admin1@nv.example.com'); + const staffUser = users.find((usr) => usr.email === 'user1@nv.example.com'); + const fetchOptions = { admin: { headers: { @@ -52,13 +57,15 @@ describe('`/api/grants` endpoint', () => { testServer = await makeTestServer(); fetchApi = testServer.fetchApi; }); + after(() => { testServer.stop(); }); const sandbox = sinon.createSandbox(); - afterEach(() => { + afterEach(async () => { sandbox.restore(); + await seed(knex); }); context('PUT api/grants/:grantId/view/:agencyId', () => { @@ -616,7 +623,6 @@ describe('`/api/grants` endpoint', () => { } const extraRowCount = Object.keys(rowsHash).length; if (extraRowCount > 0) { - console.log(JSON.stringify(rowsHash, null, 2)); expect(extraRowCount).to.equal(0); } }); @@ -724,7 +730,6 @@ HHS-2021-IHS-TPI-0001,Community Health Aide Program: Tribal Planning &`; } const extraRowCount = Object.keys(rowsHash).length; if (extraRowCount > 0) { - console.log(JSON.stringify(rowsHash, null, 2)); expect(extraRowCount).to.equal(0); } }); @@ -874,6 +879,55 @@ HHS-2021-IHS-TPI-0001,Community Health Aide Program: Tribal Planning &`; }); }); + context('GET api/grants/:grantId/notes', () => { + const GRANT_ID = '335255'; + + let notes; + beforeEach(async () => { + notes = await knex('grant_notes') + .returning('id') + .insert([ + { grant_id: GRANT_ID, user_id: adminUser.id }, + { grant_id: GRANT_ID, user_id: staffUser.id }, + ]); + + await knex('grant_notes_revisions') + .insert({ grant_note_id: notes[0].id, text: 'Test note 1.' }); + + await knex('grant_notes_revisions') + .insert({ grant_note_id: notes[1].id, text: 'Test note 2.' }); + }); + + it('returns ALL notes for a given grant in DESC order', async () => { + const resp = await fetchApi(`/grants/${GRANT_ID}/notes`, agencies.own, fetchOptions.staff); + const respBody = await resp.json(); + + expect(respBody.notes.length).to.equal(2); + expect(respBody.notes[0].text).to.equal('Test note 2.'); + }); + + it('returns notes with LIMIT', async () => { + const resp = await fetchApi(`/grants/${GRANT_ID}/notes?limit=1`, agencies.own, fetchOptions.staff); + const respBody = await resp.json(); + + expect(respBody.notes.length).to.equal(1); + }); + + it('returns 400 for invalid LIMIT', async () => { + const resp = await fetchApi(`/grants/${GRANT_ID}/notes?limit=500`, agencies.own, fetchOptions.staff); + + expect(resp.status).to.equal(400); + }); + + it('returns notes with PAGINATION', async () => { + const resp = await fetchApi(`/grants/${GRANT_ID}/notes?paginateFrom=${notes[0].id}`, agencies.own, fetchOptions.staff); + const respBody = await resp.json(); + + expect(respBody.notes.length).to.equal(1); + expect(respBody.notes[0].text).to.equal('Test note 2.'); + }); + }); + context('PUT /:grantId/notes/revision/', () => { context('by a user with admin role', () => { it('saves a new note revision for a grant', async () => { diff --git a/packages/server/seeds/dev/01_main.js b/packages/server/seeds/dev/01_main.js index a32c46296..454f22074 100644 --- a/packages/server/seeds/dev/01_main.js +++ b/packages/server/seeds/dev/01_main.js @@ -39,7 +39,7 @@ const globalCodes = [ ]; exports.seed = async (knex) => { - const tables = ['agency_eligibility_codes', 'keywords', 'eligibility_codes', 'grants', 'assigned_grants_agency', 'grants_interested', 'grants_saved_searches']; + const tables = ['agency_eligibility_codes', 'grant_notes_revisions', 'grant_followers', 'grant_notes', 'keywords', 'eligibility_codes', 'grants', 'assigned_grants_agency', 'grants_interested', 'grants_saved_searches']; // eslint-disable-next-line no-restricted-syntax for (const table of tables) { diff --git a/packages/server/src/db/index.js b/packages/server/src/db/index.js index 67d4dda58..36e4e73c5 100755 --- a/packages/server/src/db/index.js +++ b/packages/server/src/db/index.js @@ -714,8 +714,6 @@ function addCsvData(qb) { agencyId: number */ async function getGrantsNew(filters, paginationParams, orderingParams, tenantId, agencyId, toCsv) { - console.log(JSON.stringify([filters, paginationParams, orderingParams, tenantId, agencyId, toCsv])); - const errors = validateSearchFilters(filters); if (errors.length > 0) { throw new Error(`Invalid filters: ${errors.join(', ')}`); diff --git a/packages/server/src/routes/grants.js b/packages/server/src/routes/grants.js index d9fd01fdf..ec0bca270 100755 --- a/packages/server/src/routes/grants.js +++ b/packages/server/src/routes/grants.js @@ -5,7 +5,7 @@ const db = require('../db'); const email = require('../lib/email'); const { requireUser, isUserAuthorized } = require('../lib/access-helpers'); const knex = require('../db/connection'); -const { saveNoteRevision, followGrant } = require('../lib/grantsCollaboration'); +const { saveNoteRevision, followGrant, getOrganizationNotesForGrant } = require('../lib/grantsCollaboration'); const router = express.Router({ mergeParams: true }); @@ -424,6 +424,22 @@ router.delete('/:grantId/interested/:agencyId', requireUser, async (req, res) => res.json({}); }); +router.get('/:grantId/notes', requireUser, async (req, res) => { + const { grantId } = req.params; + const { user } = req.session; + const { paginateFrom, limit } = req.query; + const limitInt = limit ? parseInt(limit, 10) : undefined; + + if (limit && (!Number.isInteger(limitInt) || limitInt < 1 || limitInt > 100)) { + res.sendStatus(400); + return; + } + + const rows = await getOrganizationNotesForGrant(knex, grantId, user.tenant_id, { afterRevision: paginateFrom, limit: limitInt }); + + res.json(rows); +}); + router.put('/:grantId/notes/revision', requireUser, async (req, res) => { const { grantId } = req.params; const { user } = req.session;