Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
b0a3edb
EUI-2976
paul-graham May 20, 2021
e294b18
Update server.js
paul-graham May 20, 2021
d3a0636
Update server.js
paul-graham May 20, 2021
36714d3
Update cors.js
paul-graham May 21, 2021
f190a45
Refactor
paul-graham May 21, 2021
c72f4bb
Redis instantiator
paul-graham May 24, 2021
10b3f97
Redis persistence
paul-graham May 24, 2021
329a8b7
Track what each socket is doing
paul-graham May 24, 2021
93ae2ae
Update activity-service.js
paul-graham May 24, 2021
f479ba1
Update package.json
paul-graham May 24, 2021
9396497
Update activity-service.js
paul-graham May 25, 2021
38f84ff
Update index.js
paul-graham May 25, 2021
4bcbf05
lint
paul-graham May 25, 2021
8d853af
Update instantiator.js
paul-graham May 25, 2021
569d85b
Refactoring
paul-graham May 25, 2021
7cd2c17
Update activity-service.js
paul-graham May 25, 2021
2e4e5bc
Tests
paul-graham May 25, 2021
8c30b43
Unit tests
paul-graham May 26, 2021
46dde6a
Unit tests
paul-graham May 26, 2021
cbc6c22
Key space and tests
paul-graham May 26, 2021
b2bf897
Update keys.js
paul-graham May 27, 2021
9a0a6f2
Unit tests
paul-graham May 27, 2021
b30efab
Fixed the subscriptions
paul-graham May 27, 2021
985bdc3
Unit tests
paul-graham May 27, 2021
caee0ae
Redis subscriber
paul-graham May 28, 2021
7f8a678
Unit tests
paul-graham May 28, 2021
2762b67
moment
paul-graham May 28, 2021
3288a68
Fixed underscore CVE issue
paul-graham May 28, 2021
a4000c9
Refactor for testing
paul-graham May 28, 2021
23abf9d
Create ttl-score-generator.spec.js
paul-graham May 28, 2021
9f5a85f
Update store-cleanup-job.js
paul-graham May 28, 2021
753d42f
Update store-cleanup-job.js
paul-graham May 28, 2021
52d0395
Update server.js
paul-graham May 28, 2021
af4a7d8
CVE issue
paul-graham Jun 9, 2021
a8a177e
Socket TTLs
paul-graham Jun 10, 2021
5558216
console.logs
paul-graham Jun 10, 2021
8b5881f
Update custom-environment-variables.yaml
paul-graham Jun 14, 2021
d37a5ee
No need for 'register' event
paul-graham Jun 15, 2021
67acec7
Update activity-service.js
paul-graham Jul 15, 2021
166cdfe
Update index.js
paul-graham Jul 15, 2021
3b20e7e
Update index.spec.js
paul-graham Jul 15, 2021
b2e40f1
Version
paul-graham Jul 19, 2021
745a45f
Update values.preview.template.yaml
paul-graham Jul 19, 2021
19b59f0
Update values.preview.template.yaml
paul-graham Jul 19, 2021
32c725d
User name for logging
paul-graham Jul 28, 2021
e14501f
Merge branch 'master' into EUI-2976-exui-strategic
paulhowes-HMCTS Jan 12, 2022
15350c3
Update packages
paulhowes-HMCTS Jan 14, 2022
4bf8c81
Yarn audit
paulhowes-HMCTS Jan 14, 2022
f48e972
Known issues temporary workaround
paulhowes-HMCTS Jan 25, 2022
ec39d6b
Merge branch 'master' into EUI-2976-exui-strategic
paulhowes-HMCTS Jan 25, 2022
ebecf5c
Merge branch 'master' into EUI-2976-exui-strategic
phillip-whitaker-hmcts Oct 7, 2022
0da814d
CVE test
phillip-whitaker-hmcts Oct 7, 2022
bf49b0c
Updated CVE audit known issues file
phillip-whitaker-hmcts Oct 7, 2022
2236909
Merge branch 'master' into EUI-2976-exui-strategic
phillip-whitaker-hmcts Oct 24, 2022
7fc3372
EUI-2976: Merge master into branch and fix conflicts
LucaDelBuonoHMCTS Mar 10, 2023
799da22
EUI-2976: Fix linting
LucaDelBuonoHMCTS Mar 10, 2023
61ba69f
EUI-2976: Force rebuild
LucaDelBuonoHMCTS Mar 14, 2023
b4f0845
Resolve preview issue
danlysiak Mar 15, 2023
f24fc6a
Merge branch 'master' into EUI-2976-exui-strategic
danlysiak Mar 20, 2023
2c4f094
EUI-2976: Whitelist everything
LucaDelBuonoHMCTS Mar 20, 2023
a9b29d4
Merge branch 'EUI-2976-exui-strategic' of github.com:hmcts/ccd-case-a…
LucaDelBuonoHMCTS Mar 20, 2023
685355f
EUI-2976: Whitelist everything
LucaDelBuonoHMCTS Mar 20, 2023
8ee832d
EUI-2976: Whitelist everything
LucaDelBuonoHMCTS Mar 20, 2023
ea379e7
EUI-2976: Update node-fetch
LucaDelBuonoHMCTS Mar 21, 2023
0a79c13
EUI-2976: Reduce ttl to 30 secs for testing
LucaDelBuonoHMCTS Mar 30, 2023
3f19129
Merge branch 'master' into EUI-2976-exui-strategic
LucaDelBuonoHMCTS Apr 26, 2023
c92554e
Merge branch 'master' into EUI-2976-exui-strategic
andywilkinshmcts Jul 29, 2025
0e04b63
update dependencies
andywilkinshmcts Jul 30, 2025
c9c7a18
yarn conflic fix
balajisridharanhmcts Sep 19, 2025
242d126
yarn pkg conflict fix
balajisridharanhmcts Sep 19, 2025
0c00236
EUI-2976 activity tracker changes
balajisridharanhmcts Oct 3, 2025
faf3e64
EUI-2976 activity tracker changes
balajisridharanhmcts Oct 3, 2025
805b16f
EUI-2976 activity tracker changes
balajisridharanhmcts Oct 3, 2025
7890ecd
EUI-2976 activity tracker changes
balajisridharanhmcts Oct 3, 2025
493da28
EUI-2976 activity tracker changes
balajisridharanhmcts Oct 3, 2025
cee7995
try add redis img to preview.yaml
Josh-HMCTS Oct 3, 2025
5fe0531
test old redis ver
Josh-HMCTS Oct 3, 2025
907883a
fix indent
Josh-HMCTS Oct 3, 2025
9fbeb67
activity ttl sec increased to 3000
balajisridharanhmcts Oct 9, 2025
2482124
clean up
balajisridharanhmcts Oct 17, 2025
ce55c1d
code cleanup
balajisridharanhmcts Oct 17, 2025
c9d873d
log update
balajisridharanhmcts Nov 17, 2025
7268e03
vulnerability fix
balajisridharanhmcts Nov 18, 2025
a7a1789
stop viewing case for user added
balajisridharanhmcts Nov 21, 2025
998ad83
lint fix
balajisridharanhmcts Nov 21, 2025
735a341
socket handshake checks added
balajisridharanhmcts Dec 1, 2025
dfdba24
yarn fix
balajisridharanhmcts Dec 1, 2025
e890654
audit suppression added
balajisridharanhmcts Dec 1, 2025
d5c92f4
loggers added for socket check demo
balajisridharanhmcts Dec 10, 2025
3084f0e
lint fix
balajisridharanhmcts Dec 10, 2025
8ce74e1
yarn test fix
balajisridharanhmcts Dec 10, 2025
1db8ff2
more log added
balajisridharanhmcts Dec 10, 2025
e0e6fb1
more logs added
balajisridharanhmcts Dec 10, 2025
e6b83da
lint fix
balajisridharanhmcts Dec 10, 2025
32e6e47
more logs added
balajisridharanhmcts Dec 10, 2025
84e1965
more logs added
balajisridharanhmcts Dec 10, 2025
869c016
more logs added socket issue
balajisridharanhmcts Dec 10, 2025
416b170
websocket change
balajisridharanhmcts Dec 10, 2025
1463210
temp commented auth check
balajisridharanhmcts Dec 11, 2025
e3fc50c
auth error handling temp commented
balajisridharanhmcts Dec 11, 2025
b71e538
added error handling back
balajisridharanhmcts Dec 11, 2025
f332519
auth added again
balajisridharanhmcts Dec 11, 2025
afbadc7
lint fix
balajisridharanhmcts Dec 11, 2025
7a35ace
lint fix
balajisridharanhmcts Dec 11, 2025
19d54fc
lint fix
balajisridharanhmcts Dec 11, 2025
8df6e1b
user json log
balajisridharanhmcts Dec 11, 2025
24530fa
socket server log added
balajisridharanhmcts Dec 11, 2025
6b1e3e2
user auth uid log
balajisridharanhmcts Dec 11, 2025
361a3eb
pubsub log added
balajisridharanhmcts Dec 11, 2025
d9529dd
socket server cors update
balajisridharanhmcts Dec 11, 2025
3e38fcb
index js update socket
balajisridharanhmcts Dec 11, 2025
406074f
index js update socket
balajisridharanhmcts Dec 11, 2025
d90b96a
index js update socket
balajisridharanhmcts Dec 11, 2025
7fca32a
test lint fix
balajisridharanhmcts Dec 11, 2025
f5265c8
origin all cred false temp
balajisridharanhmcts Dec 11, 2025
b8e3ad2
dummy commit
balajisridharanhmcts Dec 12, 2025
64b3177
index js reverted
balajisridharanhmcts Dec 12, 2025
46cf9a7
socket io redis adapter added
balajisridharanhmcts Dec 12, 2025
9a370dd
lint fix
balajisridharanhmcts Dec 12, 2025
d5f67e3
handle errors added
balajisridharanhmcts Dec 12, 2025
9c04399
dummy commit
balajisridharanhmcts Dec 15, 2025
e9cb340
multi pod test
balajisridharanhmcts Dec 18, 2025
b7d9b86
Merge branch 'master' into EUI-2976-exui-strategic
RebeccaBaker Jan 12, 2026
1a90037
dummy commit
balajisridharanhmcts Jan 21, 2026
261cd8f
Merge branch 'master' into EUI-2976-exui-strategic
RebeccaBaker Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
"extends": "airbnb-base",
Comment thread
LucaDelBuonoHMCTS marked this conversation as resolved.
"env": {
"mocha": true,
"jasmine": true
},
"rules": {
"comma-dangle": 0,
"arrow-body-style": 0,
"no-param-reassign": [ 2, { props: false } ],
"linebreak-style": [ "error", process.platform === 'win32' ? 'windows' : 'unix' ]
}
}
4 changes: 0 additions & 4 deletions .eslintrc.yml

This file was deleted.

2 changes: 2 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ compressionLevel: mixed

enableGlobalCache: false

enableStrictSsl: false

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.9.4.cjs
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ $ yarn start
ccd-case-activity-api:server Listening on port 3000 +19ms
ccd-case-activity-api:redis-client connected to Redis +7ms
```

4 changes: 4 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## RELEASE NOTES

### Version 0.1.0-socket-alpha
**EUI-2976** Socket-based Activity Tracking.
16 changes: 11 additions & 5 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const healthcheck = require('@hmcts/nodejs-healthcheck');
const express = require('express');
const logger = require('morgan');
const bodyParser = require('body-parser');
const config = require('config');
const debug = require('debug')('ccd-case-activity-api:app');
const enableAppInsights = require('./app/app-insights/app-insights');
Expand Down Expand Up @@ -41,17 +40,22 @@ if (config.util.getEnv('NODE_ENV') === 'test') {
}

debug(`starting application with environment: ${config.util.getEnv('NODE_ENV')}`);
console.log(`starting application with environment: ${config.util.getEnv('NODE_ENV')}`);

app.use(corsHandler);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.text());

console.log('Applying auth checker user only filter');
app.use(authCheckerUserOnlyFilter);

console.log('Mounting activity route at /');
app.use('/', activity);

// catch 404 and forward to error handler
app.use((req, res, next) => {
console.log(`404 Not Found for request: ${req.method} ${req.originalUrl}`);
const err = new Error('Not Found');
err.status = 404;
next(err);
Expand All @@ -62,15 +66,17 @@ app.use((req, res, next) => {
/* eslint-disable no-unused-vars */
app.use((err, req, res, next) => {
debug(`Error processing request: ${err}`);
console.log(`Error processing request: ${err}`);

// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

console.log(`Returning error response: ${err.status || 500} - ${err.message}`);

res.status(err.status || 500);
res.json({
message: err.message,
});
});

module.exports = app;
1 change: 0 additions & 1 deletion app/health.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ const activityHealth = healthcheck.configure({
.catch(() => healthcheck.down())),
},
});

module.exports = activityHealth;
21 changes: 12 additions & 9 deletions app/job/store-cleanup-job.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
const cron = require('node-cron');
const debug = require('debug')('ccd-case-activity-api:store-cleanup-job');
const moment = require('moment');
const config = require('config');
const redis = require('../redis/redis-client');

const { logPipelineFailures } = redis;
const now = () => moment().valueOf();
const REDIS_ACTIVITY_KEY_PREFIX = config.get('redis.keyPrefix');

const scanExistingCasesKeys = (f) => {
const scanExistingCasesKeys = (f, prefix) => {
const stream = redis.scanStream({
// only returns keys following the pattern
match: `${REDIS_ACTIVITY_KEY_PREFIX}case:*`,
match: `${REDIS_ACTIVITY_KEY_PREFIX}${prefix}:*`,
// returns approximately 100 elements per call
count: 100,
});
Expand All @@ -28,18 +26,17 @@ const scanExistingCasesKeys = (f) => {
});
};

const getCasesWithActivities = (f) => scanExistingCasesKeys(f);
const getCasesWithActivities = (f, prefix) => scanExistingCasesKeys(f, prefix);

const cleanupActivitiesCommand = (key) => ['zremrangebyscore', key, '-inf', now()];
const cleanupActivitiesCommand = (key) => ['zremrangebyscore', key, '-inf', Date.now()];

const pipeline = (cases) => {
const commands = cases.map((caseKey) => cleanupActivitiesCommand(caseKey));
debug(`created cleanup pipeline: ${commands}`);
return redis.pipeline(commands);
};

const storeCleanup = () => {
debug('store cleanup starting...');
const cleanCasesWithPrefix = (prefix) => {
getCasesWithActivities((cases) => {
// scan returns the prefixed keys. Remove them since the redis client will add it back
const casesWithoutPrefix = cases.map((k) => k.replace(REDIS_ACTIVITY_KEY_PREFIX, ''));
Expand All @@ -50,7 +47,13 @@ const storeCleanup = () => {
.catch((err) => {
debug('Error in getCasesWithActivities', err.message);
});
});
}, prefix);
};

const storeCleanup = () => {
debug('store cleanup starting...');
cleanCasesWithPrefix('case'); // Cases via RESTful interface.
cleanCasesWithPrefix('c'); // Cases via socket interface.
};

exports.start = (crontab) => {
Expand Down
46 changes: 46 additions & 0 deletions app/redis/instantiator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const config = require('config');
const Redis = require('ioredis');

const ERROR = 0;
const RESULT = 1;
const ENV = config.util.getEnv('NODE_ENV');

module.exports = (debug) => {
const redis = new Redis({
port: config.get('redis.port'),
host: config.get('redis.host'),
password: config.get('secrets.ccd.activity-redis-password'),
tls: config.get('redis.ssl'),
keyPrefix: config.get('redis.keyPrefix'),
// log unhandled redis errors
showFriendlyErrorStack: ENV === 'test' || ENV === 'dev',
});

/* redis pipeline returns a reply of the form [[op1error, op1result], [op2error, op2result], ...].
error is null in case of success */
redis.logPipelineFailures = (plOutcome, message) => {
if (Array.isArray(plOutcome)) {
const operationsFailureOutcome = plOutcome.map((operationOutcome) => operationOutcome[ERROR]);
const failures = operationsFailureOutcome.filter((element) => element !== null);
failures.forEach((f) => debug(`${message}: ${f}`));
}
return plOutcome;
};

redis.extractPipelineResults = (pipelineOutcome) => {
const results = pipelineOutcome.map((operationOutcome) => operationOutcome[RESULT]);
debug(`pipeline results: ${results}`);
return results;
};

redis
.on('error', (err) => {
// eslint-disable-next-line no-console
debug(`Redis error: ${err.message}`);
}).on('connect', () => {
// eslint-disable-next-line no-console
debug('connected to Redis');
});

return redis;
};
46 changes: 1 addition & 45 deletions app/redis/redis-client.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,3 @@
const config = require('config');
const debug = require('debug')('ccd-case-activity-api:redis-client');
const Redis = require('ioredis');

const ERROR = 0;
const RESULT = 1;
const ENV = config.util.getEnv('NODE_ENV');

const redis = new Redis({
port: config.get('redis.port'),
host: config.get('redis.host'),
password: config.get('secrets.ccd.activity-redis-password'),
tls: config.get('redis.ssl'),
keyPrefix: config.get('redis.keyPrefix'),
// log unhandled redis errors
showFriendlyErrorStack: ENV === 'test' || ENV === 'dev',
});

/* redis pipeline returns a reply of the form [[op1error, op1result], [op2error, op2result], ...].
error is null in case of success */
redis.logPipelineFailures = (plOutcome, message) => {
if (Array.isArray(plOutcome)) {
const operationsFailureOutcome = plOutcome.map((operationOutcome) => operationOutcome[ERROR]);
const failures = operationsFailureOutcome.filter((element) => element !== null);
failures.forEach((f) => debug(`${message}: ${f}`));
} else {
debug(`${plOutcome} is not an Array...`);
}
return plOutcome;
};

redis.extractPipelineResults = (pipelineOutcome) => {
const results = pipelineOutcome.map((operationOutcome) => operationOutcome[RESULT]);
debug(`pipeline results: ${results}`);
return results;
};

redis
.on('error', (err) => {
// eslint-disable-next-line no-console
console.log(`Redis error: ${err.message}`);
}).on('connect', () => {
// eslint-disable-next-line no-console
console.log('connected to Redis');
});

module.exports = redis;
module.exports = require('./instantiator')(debug);
6 changes: 6 additions & 0 deletions app/routes/activity-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const validateRequest = require('./validate-request');
const router = express.Router();

module.exports = (activityService, config) => {
console.log(`Initializing activity route at ${new Date().toISOString()}`);

const addActivity = require('./add-activity')(activityService); // eslint-disable-line global-require
const getActivities = require('./get-activities')(activityService); // eslint-disable-line global-require

Expand All @@ -15,6 +17,8 @@ module.exports = (activityService, config) => {

router.use(timeout(toMillis(config.get('app.requestTimeoutSec'))));

console.log(`Setting request timeout to ${config.get('app.requestTimeoutSec')} seconds`);

router.post('/cases/:caseid/activity', (req, res, next) => {
validateRequest(caseIdSchema, req.params.caseid)(req, res, next);
},
Expand All @@ -25,5 +29,7 @@ module.exports = (activityService, config) => {
},
getActivities);

console.log('Activity route initialized => ready to accept requests ', router);

return router;
};
4 changes: 4 additions & 0 deletions app/routes/get-activities.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ const utils = require('../util/utils');
const { ifNotTimedOut } = utils;

const getActivities = (activityService) => (req, res, next) => {
console.log(`GET_ACTIVITIES request received at ${new Date().toISOString()}`);

const caseIds = req.params.caseids.split(',');
const { user } = req.authentication;

console.log(`GET_ACTIVITIES request for caseIds: ${caseIds}`);

debug(`GET_ACTIVITIES request for caseIds: ${caseIds}`);
activityService.getActivities(caseIds, user)
.then((result) => ifNotTimedOut(req, () => {
Expand Down
4 changes: 3 additions & 1 deletion app/routes/validate-request.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const debug = require('debug')('ccd-case-activity-api:validate-request');

const validateRequest = (schema, value) => (req, res, next) => {
const { error } = schema.validate(value);
const valid = error == null;
Expand All @@ -6,7 +8,7 @@ const validateRequest = (schema, value) => (req, res, next) => {
} else {
const { details } = error;
const message = details.map((i) => i.message).join(',');
console.log('error', message);
debug(`error ${message}`);
res.status(400).json({ error: message });
}
};
Expand Down
3 changes: 2 additions & 1 deletion app/security/cors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const config = require('config');
const sanitize = require('../util/sanitize');

const createWhitelistValidator = (val) => {
const whitelist = config.get('security.cors_origin_whitelist').split(',');
const configValue = config.get('security.cors_origin_whitelist') || '';
const whitelist = configValue.split(',');
for (let i = 0; i < whitelist.length; i += 1) {
if (val === whitelist[i]) {
return true;
Expand Down
3 changes: 1 addition & 2 deletions app/service/activity-service.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const moment = require('moment');
const debug = require('debug')('ccd-case-activity-api:activity-service');

module.exports = (config, redis, ttlScoreGenerator) => {
Expand Down Expand Up @@ -31,7 +30,7 @@ module.exports = (config, redis, ttlScoreGenerator) => {
const uniqueUserIds = [];
let caseViewers = [];
let caseEditors = [];
const now = moment.now();
const now = Date.now();
const getUserDetails = () => redis.pipeline(uniqueUserIds.map((userId) => ['get', `user:${userId}`])).exec();
const extractUniqueUserIds = (result) => {
result.forEach((item) => {
Expand Down
8 changes: 4 additions & 4 deletions app/service/ttl-score-generator.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const config = require('config');
const moment = require('moment');
const debug = require('debug')('ccd-case-activity-api:score-generator');

exports.getScore = () => {
const now = moment();
const score = now.add(config.get('redis.activityTtlSec'), 'seconds').valueOf();
debug(`generated score out of current timestamp '${now.valueOf()}' plus ${config.get('redis.activityTtlSec')} sec`);
const now = Date.now();
const ttl = parseInt(config.get('redis.activityTtlSec'), 10) || 0;
const score = now + (ttl * 1000);
debug(`generated score out of current timestamp '${now}' plus ${ttl} sec`);
return score;
};
Loading