Skip to content

Commit

Permalink
enrich assessment with approver details
Browse files Browse the repository at this point in the history
  • Loading branch information
abrantesarthur committed Jan 21, 2025
1 parent eea8129 commit bb87789
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 11 deletions.
23 changes: 22 additions & 1 deletion src/oneTrust/codecs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
OneTrustApprover,
OneTrustAssessment,
OneTrustAssessmentNestedQuestion,
OneTrustAssessmentQuestion,
Expand Down Expand Up @@ -166,16 +167,36 @@ export type OneTrustAssessmentCreatedBy = t.TypeOf<
typeof OneTrustAssessmentCreatedBy
>;

export const OneTrustUserDetails = t.type({
active: OneTrustGetUserResponse.props.active,
userType: OneTrustGetUserResponse.props.userType,
emails: OneTrustGetUserResponse.props.emails,
title: OneTrustGetUserResponse.props.title,
givenName: t.union([t.string, t.null]),
familyName: t.union([t.string, t.null]),
});
/** Type override */
export type OneTrustUserDetails = t.TypeOf<typeof OneTrustUserDetails>;

export const OneTrustEnrichedUser = t.type({
...OneTrustAssessmentCreatedBy.props,
...OneTrustGetUserResponse.props,
...OneTrustUserDetails.props,
});

/** Type override */
export type OneTrustEnrichedUser = t.TypeOf<typeof OneTrustEnrichedUser>;

export const OneTrustEnrichedAssessmentResponse = t.type({
...OneTrustGetAssessmentResponse.props,
approvers: t.array(
t.type({
...OneTrustApprover.props,
approver: t.type({
...OneTrustApprover.props.approver.props,
...OneTrustUserDetails.props,
}),
}),
),
createdBy: OneTrustEnrichedUser,
sections: t.array(OneTrustEnrichedAssessmentSection),
});
Expand Down
9 changes: 4 additions & 5 deletions src/oneTrust/endpoints/getOneTrustUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { decodeCodec } from '@transcend-io/type-utils';
/**
* FIXME: move to privacy-types
*/

// Nested types first
const OneTrustUserMetadata = t.type({
/** When the user was created */
Expand All @@ -20,7 +19,7 @@ const OneTrustUserMetadata = t.type({
/** Type override */
export type OneTrustUserMetadata = t.TypeOf<typeof OneTrustUserMetadata>;

const OneTrustUserName = t.type({
const OneTrustUserName = t.partial({
/** The user's family name */
familyName: t.string,
/** The user's given name */
Expand Down Expand Up @@ -104,14 +103,14 @@ export type OneTrustGetUserResponse = t.TypeOf<typeof OneTrustGetUserResponse>;
*/
export const getOneTrustUser = async ({
oneTrust,
creatorId,
userId,
}: {
/** The OneTrust client instance */
oneTrust: Got;
/** The ID of the OneTrust user to retrieve */
creatorId: string;
userId: string;
}): Promise<OneTrustGetUserResponse> => {
const { body } = await oneTrust.get(`api/scim/v2/Users/${creatorId}`);
const { body } = await oneTrust.get(`api/scim/v2/Users/${userId}`);

return decodeCodec(OneTrustGetUserResponse, body);
};
29 changes: 28 additions & 1 deletion src/oneTrust/helpers/enrichOneTrustAssessment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const enrichOneTrustAssessment = ({
assessmentDetails,
riskDetails,
creatorDetails,
approversDetails,
}: {
/** The OneTrust risk details */
riskDetails: OneTrustGetRiskResponse[];
Expand All @@ -27,6 +28,8 @@ export const enrichOneTrustAssessment = ({
assessmentDetails: OneTrustGetAssessmentResponse;
/** The OneTrust assessment creator details */
creatorDetails: OneTrustGetUserResponse;
/** The OneTrust assessment approvers details */
approversDetails: OneTrustGetUserResponse[];
}): OneTrustEnrichedAssessment => {
const riskDetailsById = keyBy(riskDetails, 'id');
const { sections, createdBy, ...restAssessmentDetails } = assessmentDetails;
Expand Down Expand Up @@ -63,14 +66,38 @@ export const enrichOneTrustAssessment = ({

const enrichedCreatedBy = {
...createdBy,
...creatorDetails,
active: creatorDetails.active,
userType: creatorDetails.userType,
emails: creatorDetails.emails,
title: creatorDetails.title,
givenName: creatorDetails.name.givenName ?? null,
familyName: creatorDetails.name.familyName ?? null,
};

const approverDetailsById = keyBy(approversDetails, 'id');
const enrichedApprovers = assessmentDetails.approvers.map(
(originalApprover) => ({
...originalApprover,
approver: {
...originalApprover.approver,
active: approverDetailsById[originalApprover.id].active,
userType: approverDetailsById[originalApprover.id].userType,
emails: approverDetailsById[originalApprover.id].emails,
title: approverDetailsById[originalApprover.id].title,
givenName:
approverDetailsById[originalApprover.id].name.givenName ?? null,
familyName:
approverDetailsById[originalApprover.id].name.familyName ?? null,
},
}),
);

// combine the two assessments into a single enriched result

return {
...assessment,
...restAssessmentDetails,
approvers: enrichedApprovers,
createdBy: enrichedCreatedBy,
sections: sectionsWithEnrichedRisk,
};
Expand Down
32 changes: 28 additions & 4 deletions src/oneTrust/helpers/syncOneTrustAssessments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const syncOneTrustAssessments = async ({
const assessments = await getListOfOneTrustAssessments({ oneTrust });

// a cache of OneTrust users so we avoid sending requests for users already fetched
const allOneTrustUsers: Record<string, OneTrustGetUserResponse> = {};
const oneTrustCachedUsers: Record<string, OneTrustGetUserResponse> = {};

/**
* fetch details about each assessment in series and write to transcend or to disk
Expand All @@ -75,12 +75,35 @@ export const syncOneTrustAssessments = async ({
);
const creatorId = assessmentDetails.createdBy.id;
const creator =
allOneTrustUsers[creatorId] ??
oneTrustCachedUsers[creatorId] ??
(await getOneTrustUser({
oneTrust,
creatorId: assessmentDetails.createdBy.id,
userId: creatorId,
}));
allOneTrustUsers[creatorId] = creator;
oneTrustCachedUsers[creatorId] = creator;

// fetch assessment approvers information
const { approvers } = assessmentDetails;
let approversDetails: OneTrustGetUserResponse[] = [];
if (approvers.length > 0) {
logger.info(
`Fetching details about ${approvers.length} approvers for assessment ${
index + 1
} of ${assessments.length}...`,
);
approversDetails = await map(
approvers.map(({ id }) => id),
async (userId) => {
let approver = oneTrustCachedUsers[userId];
if (!approver) {
approver = await getOneTrustUser({ oneTrust, userId });
oneTrustCachedUsers[userId] = approver;
}
return approver;
},
{ concurrency: 5 },
);
}

// fetch assessment risk information
let riskDetails: OneTrustGetRiskResponse[] = [];
Expand Down Expand Up @@ -112,6 +135,7 @@ export const syncOneTrustAssessments = async ({
assessmentDetails,
riskDetails,
creatorDetails: creator,
approversDetails,
});

if (dryRun && file && fileFormat) {
Expand Down

0 comments on commit bb87789

Please sign in to comment.