Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion app/routes/get-activities.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ const { ifNotTimedOut } = utils;
const getActivities = (activityService) => (req, res, next) => {
const caseIds = req.params.caseids.split(',');
const { user } = req.authentication;
const bearerToken = req.get('Authorization');

debug(`GET_ACTIVITIES request for caseIds: ${caseIds}`);
activityService.getActivities(caseIds, user)
activityService.getActivities(caseIds, user, bearerToken)
.then((result) => ifNotTimedOut(req, () => {
debug(`GET_ACTIVITIES response is ==> ${JSON.stringify(result)}`);
res.status(200).json(result);
Expand Down
82 changes: 81 additions & 1 deletion app/service/activity-service.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,68 @@
const moment = require('moment');
const debug = require('debug')('ccd-case-activity-api:activity-service');
const fetch = require('../util/fetch');
const jwtUtil = require('../util/jwt');

module.exports = (config, redis, ttlScoreGenerator) => {
const redisActivityKeys = {
view: (caseId) => `case:${caseId}:viewers`,
edit: (caseId) => `case:${caseId}:editors`,
};

const ACCESS_PROCESS_ID = '[ACCESS_PROCESS]';
const ACCESS_GRANTED_ID = '[ACCESS_GRANTED]';
const BASIC_ACCESS = 'BASIC';
const NON_STANDARD_ACCESS_TYPES = ['CHALLENGED', 'SPECIFIC'];
const CASE_VIEW_ACCEPT = 'application/vnd.uk.gov.hmcts.ccd-data-store-api.ui-case-view.v2+json';

const buildEmptyCaseStatus = (caseId) => ({
caseId,
viewers: [],
unknownViewers: 0,
editors: [],
unknownEditors: 0,
});

const isStandardAccess = (caseView) => {
if (!caseView || !Array.isArray(caseView.metadataFields)) {
return true;
}

const accessProcess = caseView.metadataFields
.find((metadataField) => metadataField.id === ACCESS_PROCESS_ID);
const accessGranted = caseView.metadataFields
.find((metadataField) => metadataField.id === ACCESS_GRANTED_ID);

const accessGrantedValue = accessGranted ? accessGranted.value : null;
const accessGrantedBool = accessGrantedValue ? accessGrantedValue !== BASIC_ACCESS : false;
const accessProcessValue = accessProcess ? accessProcess.value : null;

return accessGrantedBool || NON_STANDARD_ACCESS_TYPES.indexOf(accessProcessValue) === -1;
};

const getCaseView = (caseId, bearerToken) => {
const baseUrl = config.get('ccd.url');
const url = `${baseUrl}/internal/cases/${caseId}`;
return fetch(url, {
headers: {
Authorization: jwtUtil.addBearer(bearerToken),
Accept: CASE_VIEW_ACCEPT,
},
}).then((res) => res.json());
};

const hasStandardAccess = (caseId, bearerToken) => {
if (!bearerToken) {
return Promise.resolve(false);
}
return getCaseView(caseId, bearerToken)
.then((caseView) => isStandardAccess(caseView))
.catch((error) => {
debug(`Access check failed for caseId ${caseId}: ${error && error.message ? error.message : error}`);
return false;
});
};

const addActivity = (caseId, user, activity) => {
const storeUserActivity = () => {
const key = redisActivityKeys[activity](caseId);
Expand All @@ -27,7 +83,7 @@ module.exports = (config, redis, ttlScoreGenerator) => {
]).exec();
};

const getActivities = (caseIds, user) => {
const getActivitiesForCaseIds = (caseIds, user) => {
const uniqueUserIds = [];
let caseViewers = [];
let caseEditors = [];
Expand Down Expand Up @@ -83,5 +139,29 @@ module.exports = (config, redis, ttlScoreGenerator) => {
return caseStatus;
}));
};

const getActivities = (caseIds, user, bearerToken) => {
if (!caseIds || caseIds.length === 0) {
return Promise.resolve([]);
}

const accessChecks = caseIds.map((caseId) => hasStandardAccess(caseId, bearerToken)
.then((allowed) => ({ caseId, allowed })));

return Promise.all(accessChecks)
.then((accessResults) => {
const allowedCaseIds = accessResults
.filter((result) => result.allowed)
.map((result) => result.caseId);
if (!allowedCaseIds.length) {
return caseIds.map((caseId) => buildEmptyCaseStatus(caseId));
}
return getActivitiesForCaseIds(allowedCaseIds, user)
.then((allowedResults) => {
const allowedMap = new Map(allowedResults.map((item) => [item.caseId, item]));
return caseIds.map((caseId) => allowedMap.get(caseId) || buildEmptyCaseStatus(caseId));
});
});
};
return { addActivity, getActivities };
};
2 changes: 1 addition & 1 deletion charts/ccd-case-activity-api/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
description: Helm chart for the HMCTS CCD Case Activity
name: ccd-case-activity-api
home: https://github.com/hmcts/ccd-case-activity-api
version: 1.3.17
version: 1.3.18
maintainers:
- name: HMCTS CCD Dev Team
email: ccd-devops@HMCTS.NET
Expand Down
1 change: 1 addition & 0 deletions charts/ccd-case-activity-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ nodejs:
AUTH_WHITE_LIST: ^caseworker-.+
AUTH_BLACK_LIST: solicitor
IDAM_BASE_URL: https://idam-api.{{ .Values.global.environment }}.platform.hmcts.net
SERVICES_CCD_COMPONENT_API: https://gateway-ccd.{{ .Values.global.environment }}.platform.hmcts.net
REDIS_HOST: ccd-activity-service-{{ .Values.global.environment }}.redis.cache.windows.net
REDIS_PORT: 6380
REDIS_SSL_ENABLED: true
Expand Down
2 changes: 2 additions & 0 deletions config/custom-environment-variables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ security:
auth_blacklist: AUTH_BLACK_LIST
idam:
base_url: IDAM_BASE_URL
ccd:
url: SERVICES_CCD_COMPONENT_API
redis:
host: REDIS_HOST
port: REDIS_PORT
Expand Down
11 changes: 7 additions & 4 deletions test/spec/app/route/get-activities.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ describe("get activities route", () => {
let req = {
params: { caseids: '111, 121' },
authentication: { user: { id: 900 } },
timedout: false
timedout: false,
get: (header) => (header === 'Authorization' ? 'Bearer token' : undefined)
};
let next = () => {
};
Expand All @@ -42,7 +43,7 @@ describe("get activities route", () => {

getActivitesRoute(req, res, next)

expect(activityService.getActivities).to.have.been.calledWith(req.params.caseids.split(','), { id : 900 })
expect(activityService.getActivities).to.have.been.calledWith(req.params.caseids.split(','), { id : 900 }, 'Bearer token')
})

it("should not return a result when request is successful after it has timed out", (done) => {
Expand All @@ -52,7 +53,8 @@ describe("get activities route", () => {
caseids: '111,121'
},
authentication: { user: { id: 900 } },
timedout: true
timedout: true,
get: (header) => (header === 'Authorization' ? 'Bearer token' : undefined)
};

let next = () => {
Expand All @@ -79,7 +81,8 @@ describe("get activities route", () => {
caseids: '111,121'
},
authentication: { user: { id: 900 } },
timedout: true
timedout: true,
get: (header) => (header === 'Authorization' ? 'Bearer token' : undefined)
};

var res = buildResponse()
Expand Down
Loading