Skip to content

Commit

Permalink
add ability to sync to Transcend
Browse files Browse the repository at this point in the history
  • Loading branch information
abrantesarthur committed Jan 14, 2025
1 parent df76064 commit b424c03
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 27 deletions.
31 changes: 27 additions & 4 deletions src/cli-sync-ot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import colors from 'colors';
import { parseCliSyncOtArguments, createOneTrustGotInstance } from './oneTrust';
import { OneTrustPullResource } from './enums';
import { syncOneTrustAssessments } from './oneTrust/helpers/syncOneTrustAssessments';
import { buildTranscendGraphQLClient } from './graphql';

/**
* Pull configuration from OneTrust down locally to disk
Expand All @@ -16,15 +17,37 @@ import { syncOneTrustAssessments } from './oneTrust/helpers/syncOneTrustAssessme
* yarn cli-sync-ot --hostname=customer.my.onetrust.com --auth=$ONE_TRUST_OAUTH_TOKEN --file=./oneTrustAssessment.json
*/
async function main(): Promise<void> {
const { file, fileFormat, hostname, auth, resource, debug, dryRun } =
parseCliSyncOtArguments();
const {
file,
fileFormat,
hostname,
oneTrustAuth,
transcendAuth,
transcendUrl,
resource,
debug,
dryRun,
} = parseCliSyncOtArguments();

// use the hostname and auth token to instantiate a client to talk to OneTrust
const oneTrust = createOneTrustGotInstance({ hostname, auth });
const oneTrust = createOneTrustGotInstance({ hostname, auth: oneTrustAuth });

try {
if (resource === OneTrustPullResource.Assessments) {
await syncOneTrustAssessments({ oneTrust, file, fileFormat, dryRun });
await syncOneTrustAssessments({
oneTrust,
file,
fileFormat,
dryRun,
...(transcendAuth && transcendUrl
? {
transcend: buildTranscendGraphQLClient(
transcendUrl,
transcendAuth,
),
}
: {}),
});
}
} catch (err) {
logger.error(
Expand Down
28 changes: 28 additions & 0 deletions src/codecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2073,3 +2073,31 @@ export const PathfinderPromptRunMetadata = t.partial({
export type PathfinderPromptRunMetadata = t.TypeOf<
typeof PathfinderPromptRunMetadata
>;

/** The columns of a row of a OneTrust Assessment form to import into Transcend. */
const OneTrustAssessmentColumnInput = t.intersection([
t.type({
/** The title of the column */
title: t.string,
}),
t.partial({
/** The optional value of the column */
value: t.string,
}),
]);

/** A row with information of the OneTrust assessment form to import into Transcend */
const OneTrustAssessmentRowInput = t.type({
/** A list of columns within this row. */
columns: t.array(OneTrustAssessmentColumnInput),
});

/** Input for importing multiple OneTrust assessment forms into Transcend */
export const ImportOnetrustAssessmentsInput = t.type({
/** 'The rows of the CSV file.' */
rows: t.array(OneTrustAssessmentRowInput),
});
/** Type override */
export type ImportOnetrustAssessmentsInput = t.TypeOf<
typeof ImportOnetrustAssessmentsInput
>;
13 changes: 13 additions & 0 deletions src/graphql/gqls/assessment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,16 @@ export const ASSESSMENTS = gql`
}
}
`;

export const IMPORT_ONE_TRUST_ASSESSMENT_FORMS = gql`
mutation TranscendCliImportOneTrustAssessmentForms(
$input: ImportOnetrustAssessmentsInput!
) {
importOneTrustAssessmentForms(input: $input) {
assessmentForms {
id
title
}
}
}
`;
2 changes: 1 addition & 1 deletion src/oneTrust/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './flattenOneTrustAssessment';
export * from './parseCliSyncOtArguments';
export * from './writeOneTrustAssessment';
export * from './syncOneTrustAssessmentToDisk';
38 changes: 22 additions & 16 deletions src/oneTrust/helpers/parseCliSyncOtArguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ interface OneTrustCliArguments {
oneTrustAuth: string;
/** The Transcend API key to authenticate the requests to Transcend */
transcendAuth: string;
/** The Transcend URL where to forward requests */
transcendUrl: string;
/** The resource to pull from OneTrust */
resource: OneTrustPullResource;
/** Whether to enable debugging while reporting errors */
Expand Down Expand Up @@ -114,22 +116,25 @@ export const parseCliSyncOtArguments = (): OneTrustCliArguments => {
return process.exit(1);
}

const splitFile = file.split('.');
if (splitFile.length < 2) {
logger.error(
colors.red(
'The "file" parameter has an invalid format. Expected a path with extensions. e.g. --file=./pathToFile.json.',
),
);
return process.exit(1);
}
if (splitFile.at(-1) !== fileFormat) {
logger.error(
colors.red(
`The "file" and "fileFormat" parameters must specify the same format! Got file=${file} and fileFormat=${fileFormat}`,
),
);
return process.exit(1);
if (file) {
const splitFile = file.split('.');
if (splitFile.length < 2) {
logger.error(
colors.red(
'The "file" parameter has an invalid format. Expected a path with extensions. e.g. --file=./pathToFile.json.',
),
);
return process.exit(1);
}
if (splitFile.at(-1) !== fileFormat) {
logger.error(
colors.red(
// eslint-disable-next-line max-len
`The "file" and "fileFormat" parameters must specify the same format! Got file=${file} and fileFormat=${fileFormat}`,
),
);
return process.exit(1);
}
}

if (!hostname) {
Expand Down Expand Up @@ -179,5 +184,6 @@ export const parseCliSyncOtArguments = (): OneTrustCliArguments => {
fileFormat,
dryRun,
transcendAuth,
transcendUrl,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { oneTrustAssessmentToCsv } from './oneTrustAssessmentToCsv';
*
* @param param - information about the assessment to write
*/
export const writeOneTrustAssessment = ({
export const syncOneTrustAssessmentToDisk = ({
file,
fileFormat,
assessment,
Expand Down
63 changes: 63 additions & 0 deletions src/oneTrust/helpers/syncOneTrustAssessmentToTranscend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { logger } from '../../logger';
import { oneTrustAssessmentToCsvRecord } from './oneTrustAssessmentToCsvRecord';
import { GraphQLClient } from 'graphql-request';
import {
IMPORT_ONE_TRUST_ASSESSMENT_FORMS,
makeGraphQLRequest,
} from '../../graphql';
import { ImportOnetrustAssessmentsInput } from '../../codecs';
import { OneTrustEnrichedAssessment } from '../codecs';

export interface AssessmentForm {
/** ID of Assessment Form */
id: string;
/** Title of Assessment Form */
name: string;
}

/**
* Write the assessment to a Transcend instance.
*
*
* @param param - information about the assessment and Transcend instance to write to
*/
export const syncOneTrustAssessmentToTranscend = async ({
transcend,
assessment,
}: {
/** the Transcend client instance */
transcend: GraphQLClient;
/** the assessment to sync to Transcend */
assessment: OneTrustEnrichedAssessment;
}): Promise<AssessmentForm | undefined> => {
logger.info();

// convert the OneTrust assessment object into a CSV Record (a map from the csv header to values)
const csvRecord = oneTrustAssessmentToCsvRecord(assessment);

// transform the csv record into a valid input to the mutation
const input: ImportOnetrustAssessmentsInput = {
rows: [
{
columns: Object.entries(csvRecord).map(([key, value]) => ({
title: key,
value: value.toString(),
})),
},
],
};

const {
importOneTrustAssessmentForms: { assessmentForms },
} = await makeGraphQLRequest<{
/** the importOneTrustAssessmentForms mutation */
importOneTrustAssessmentForms: {
/** Created Assessment Forms */
assessmentForms: AssessmentForm[];
};
}>(transcend, IMPORT_ONE_TRUST_ASSESSMENT_FORMS, {
input,
});

return assessmentForms[0];
};
24 changes: 19 additions & 5 deletions src/oneTrust/helpers/syncOneTrustAssessments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,29 @@ import {
} from '@transcend-io/privacy-types';
import uniq from 'lodash/uniq';
import { enrichOneTrustAssessment } from './enrichOneTrustAssessment';
import { writeOneTrustAssessment } from './writeOneTrustAssessment';
import { syncOneTrustAssessmentToDisk } from './syncOneTrustAssessmentToDisk';
import { OneTrustFileFormat } from '../../enums';
import { oneTrustAssessmentToCsvRecord } from './oneTrustAssessmentToCsvRecord';
import { GraphQLClient } from 'graphql-request';
import { syncOneTrustAssessmentToTranscend } from './syncOneTrustAssessmentToTranscend';

export interface AssessmentForm {
/** ID of Assessment Form */
id: string;
/** Title of Assessment Form */
name: string;
}

export const syncOneTrustAssessments = async ({
oneTrust,
file,
fileFormat,
dryRun,
transcend,
}: {
/** the OneTrust client instance */
oneTrust: Got;
/** the Transcend client instance */
transcend?: GraphQLClient;
/** Whether to write to file instead of syncing to Transcend */
dryRun: boolean;
/** the path to the file in case dryRun is true */
Expand Down Expand Up @@ -84,16 +95,19 @@ export const syncOneTrustAssessments = async ({

if (dryRun && file && fileFormat) {
// sync to file
writeOneTrustAssessment({
syncOneTrustAssessmentToDisk({
assessment: enrichedAssessment,
index,
total: assessments.length,
file,
fileFormat,
});
} else if (fileFormat === OneTrustFileFormat.Csv) {
} else if (fileFormat === OneTrustFileFormat.Csv && transcend) {
// sync to transcend
// const csvEntry = oneTrustAssessmentToCsvRecord(enrichedAssessment);
await syncOneTrustAssessmentToTranscend({
assessment: enrichedAssessment,
transcend,
});
}
});
};

0 comments on commit b424c03

Please sign in to comment.