Skip to content

Commit

Permalink
Merge branch 'main' into fix/1500-seeing-service-capacity-when-it-is-…
Browse files Browse the repository at this point in the history
…not-applicable
  • Loading branch information
ssrome committed Sep 10, 2024
2 parents 8973b3a + 19c1339 commit c51a98f
Show file tree
Hide file tree
Showing 13 changed files with 577 additions and 15,023 deletions.
15,454 changes: 473 additions & 14,981 deletions backend/package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@
"exceljs": "^4.2.0",
"express": "^4.19.2",
"express-http-proxy": "^1.6.2",
"express-rate-limit": "^5.5.1",
"express-rate-limit": "^7.4.0",
"express-sanitizer": "^1.0.6",
"helmet": "^4.6.0",
"ioredis": "^5.4.1",
"is-ci": "^3.0.0",
"js-yaml": "^3.14.1",
"jsonwebtoken": "^9.0.2",
Expand All @@ -83,7 +84,7 @@
"pg": "^8.5.1",
"pg-hstore": "^2.3.4",
"pug": "^3.0.1",
"rate-limit-redis": "^2.1.0",
"rate-limit-redis": "^4.2.0",
"request": "^2.88.2",
"sequelize": "^6.29.0",
"sequelize-cli": "^6.6.0",
Expand Down
4 changes: 1 addition & 3 deletions backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,7 @@ app.get('/loaderio-63e80cd3c669177f22e9ec997ea2594d.txt', function (req, res) {
});

app.use('*', authLimiter);
// app.get('*', function (req, res) {
// return res.sendFile(path.join(__dirname, 'dist/index.html'));
// });
app.set('trust proxy', 2 /* number of proxies between user and server */);

app.all('*', function (req, res) {
res.status(404);
Expand Down
4 changes: 2 additions & 2 deletions backend/server/routes/nhsBsaApi/apiDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const express = require('express');
const router = express.Router();
const fs = require('fs');
const path = require('path');
const { authLimiter } = require('../../utils/middleware/rateLimiting');
const { nhsBsaApiLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI');

const nhsBsaApiDocumentation = (req, res) => {
try {
Expand All @@ -17,6 +17,6 @@ const nhsBsaApiDocumentation = (req, res) => {
}
};

router.route('/').get(authLimiter, nhsBsaApiDocumentation);
router.route('/').get(nhsBsaApiLimiter, nhsBsaApiDocumentation);
module.exports = router;
module.exports.nhsBsaApiDocumentation = nhsBsaApiDocumentation;
3 changes: 2 additions & 1 deletion backend/server/routes/nhsBsaApi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ const express = require('express');
const router = express.Router();
const generateJWT = require('../../utils/security/NHSBSAgenerateJWT');
const config = require('../../config/config');
const { nhsBsaApiLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI');

router.use('/', nhsBsaApiLimiter);
router.route('/').get(function (req, res) {

const authHeader = req.headers && req.headers.nhsbsaapikey;
const nhsBsaApiKey = config.get('nhsBsaApi.apikey');

Expand Down
4 changes: 2 additions & 2 deletions backend/server/routes/nhsBsaApi/workplaceData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const express = require('express');
const router = express.Router();
const models = require('../../models');
const authorization = require('../../utils/middleware/isNHSBSAAuthenticated');
const { authLimiter } = require('../../utils/middleware/rateLimiting');
const { nhsBsaApiLimiter } = require('../../utils/middleware/rateLimitingNHSBSAAPI');
// WDF effective date
const WdfCalculator = require('../../models/classes/wdfCalculator').WdfCalculator;

Expand Down Expand Up @@ -98,7 +98,7 @@ const calculatePercentageOfWorkersEligible = (workers) => {
return Math.floor((numberOfEligibleWorkers / numberOfWorkers) * 100);
};

router.route('/:workplaceId').get(authLimiter, authorization.isAuthorised, nhsBsaApi);
router.route('/:workplaceId').get(nhsBsaApiLimiter, authorization.isAuthorised, nhsBsaApi);
module.exports = router;
module.exports.nhsBsaApi = nhsBsaApi;
module.exports.subsidiariesList = subsidiariesList;
3 changes: 2 additions & 1 deletion backend/server/routes/reports/deleteReport/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const fillData = (reportData, laData, WS1) => {
la = laData[establishment.id].cssrRecord.localAuthority;
}
const address = concatenateAddress(
establishment.address,
establishment.address1,
establishment.address2,
establishment.address3,
establishment.town,
Expand Down Expand Up @@ -161,4 +161,5 @@ const generateDeleteReport = async (req, res) => {

module.exports = router;
module.exports.generateDeleteReport = generateDeleteReport;
module.exports.fillData = fillData;
module.exports.monthsWithoutUpdate = monthsWithoutUpdate;
2 changes: 1 addition & 1 deletion backend/server/test/factories/deleteReport/deleteReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const rawDataBuilder = build('rawData', {
isRegulated: false,
address1: fake((f) => f.address.streetAddress()),
address2: fake((f) => f.address.secondaryAddress()),
address3: null,
address3: 'Third Address Line',
town: fake((f) => f.address.city()),
county: fake((f) => f.address.county()),
postcode: fake((f) => f.address.zipCode('??# #??')),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const expect = require('chai').expect;
const sinon = require('sinon');
const httpMocks = require('node-mocks-http');
const excelJS = require('exceljs');

const deleteReport = require('../../../../../routes/reports/deleteReport/report');
const models = require('../../../../../models');
Expand All @@ -11,7 +12,16 @@ const { rawDataBuilder } = require('../../../../factories/deleteReport/deleteRep

describe('/server/routes/reports/deleteReport/report', () => {
describe('deleteReport()', () => {
let req;
let res;

beforeEach(() => {
req = httpMocks.createRequest({
method: 'GET',
url: '/api/report/deleteReport/report',
});
res = httpMocks.createResponse();

sinon.stub(models.pcodedata, 'getLinkedCssrRecordsFromPostcode').callsFake(async () => {
return {};
});
Expand All @@ -24,11 +34,6 @@ describe('/server/routes/reports/deleteReport/report', () => {
sinon.stub(models.establishment, 'generateDeleteReportData').callsFake(async () => {
return [rawDataBuilder(), rawDataBuilder(), rawDataBuilder()];
});
const req = httpMocks.createRequest({
method: 'GET',
url: '/api/report/deleteReport/report',
});
const res = httpMocks.createResponse();

await deleteReport.generateDeleteReport(req, res);

Expand All @@ -37,5 +42,38 @@ describe('/server/routes/reports/deleteReport/report', () => {
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
});

describe('fillData', () => {
let workbook;
let mockWorksheet;

beforeEach(() => {
workbook = new excelJS.Workbook();

mockWorksheet = workbook.addWorksheet('To be deleted', { views: [{ showGridLines: false }] });
});

it('should fill next B cell with workplace name', async () => {
const workplaceData = rawDataBuilder();
const expectedWorkplaceName = workplaceData.NameValue;

deleteReport.fillData([workplaceData], {}, mockWorksheet);

expect(mockWorksheet.getCell('B1').value).to.equal(expectedWorkplaceName);
});

it('should fill next C cell with concatenated address lines', async () => {
const workplaceData = rawDataBuilder();

deleteReport.fillData([workplaceData], {}, mockWorksheet);

const addressCell = mockWorksheet.getCell('C1').value;
expect(addressCell).to.include(workplaceData.address1);
expect(addressCell).to.include(workplaceData.address2);
expect(addressCell).to.include(workplaceData.address3);
expect(addressCell).to.include(workplaceData.town);
expect(addressCell).to.include(workplaceData.county);
});
});
});
});
17 changes: 17 additions & 0 deletions backend/server/test/unit/utils/concatenateAddress.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const expect = require('chai').expect;

const concatenateAddress = require('../../../utils/concatenateAddress').concatenateAddress;

describe('concatenateAddress', () => {
it('should only return address line 1 when only line 1 passed in', () => {
expect(concatenateAddress('Test Address Line 1')).to.equal('Test Address Line 1');
});

it('should concatenate all lines with spaces between when all provided', () => {
expect(concatenateAddress('A', 'B', 'C', 'D', 'E')).to.equal('A B C D E');
});

it('should concatenate with single spaces between when address lines 2 and 3 are missing but town and county exist', () => {
expect(concatenateAddress('A', undefined, undefined, 'D', 'E')).to.equal('A D E');
});
});
5 changes: 2 additions & 3 deletions backend/server/utils/concatenateAddress.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
exports.concatenateAddress = function (addressLine1, addressLine2, townAndCity, county) {

//Remove whitespaces and any non alphanumeric characters and then cast to upper case.
exports.concatenateAddress = function (addressLine1, addressLine2, addressLine3, townAndCity, county) {
let concatAddress = '';

if (addressLine1) concatAddress += addressLine1;
if (addressLine2) concatAddress += ' ' + addressLine2;
if (addressLine3) concatAddress += ' ' + addressLine3;
if (townAndCity) concatAddress += ' ' + townAndCity;
if (county) concatAddress += ' ' + county;

Expand Down
27 changes: 18 additions & 9 deletions backend/server/utils/middleware/rateLimiting.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
const config = require('../../config/config');
const RateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const expressRateLimit = require('express-rate-limit');
const { RedisStore } = require('rate-limit-redis');
const RedisClient = require('ioredis');
const isCI = require('is-ci');

const store = isCI
const redisClient = new RedisClient(config.get('redis.url'));
const authStore = isCI
? undefined
: new RedisStore({
redisURL: config.get('redis.url'),
sendCommand: (...args) => redisClient.call(...args),
});

const dbStore = isCI
? undefined
: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
});

const rateLimiterConfig = {
store,
delayMs: 0, // disable delaying - full speed until the max limit is reached
passIfNotConnected: true,
windowMs: 5000,
Expand All @@ -20,19 +27,21 @@ const authLimiter = isCI
? (req, res, next) => {
next();
}
: new RateLimit({
: expressRateLimit.rateLimit({
...rateLimiterConfig,
max: 1000,
store: authStore,
limit: 1000,
prefix: 'auth:',
});

const dbLimiter = isCI
? (req, res, next) => {
next();
}
: new RateLimit({
: expressRateLimit.rateLimit({
...rateLimiterConfig,
max: 1000,
store: dbStore,
limit: 1000,
prefix: 'db:',
});

Expand Down
24 changes: 11 additions & 13 deletions backend/server/utils/middleware/rateLimitingNHSBSAAPI.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@

const config = require('../../config/config');
const RateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const expressRateLimit = require('express-rate-limit');
const { RedisStore } = require('rate-limit-redis');
const RedisClient = require('ioredis');
const isCI = require('is-ci');

const redisClient = new RedisClient(config.get('redis.url'));
const store = isCI
? undefined
: new RedisStore({
redisURL: config.get('redis.url'),
sendCommand: (...args) => redisClient.call(...args),
});

const rateLimiterConfig = {
store,
delayMs: 0, // disable delaying - full speed until the max limit is reached
passIfNotConnected: true,
windowMs: 60 * 1000, // 1 minute
windowMs: 60 * 1000, // 1 minute
};

const authLimiter = isCI
const nhsBsaApiLimiter = isCI
? (req, res, next) => {
next();
}
: new RateLimit({
: expressRateLimit.rateLimit({
...rateLimiterConfig,
max: 100, // maximum number of requests allowed in the windowMs
prefix: 'auth:',
limit: 200, // maximum number of requests allowed in the windowMs
prefix: 'nhsBsaApi:',
});



module.exports.authLimiter = authLimiter;

module.exports.nhsBsaApiLimiter = nhsBsaApiLimiter;

0 comments on commit c51a98f

Please sign in to comment.