Skip to content

[WIP] Push purposes #405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
65 changes: 34 additions & 31 deletions README.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions examples/purposes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ purposes:
- DNT
auth-level: REQUIRED
preference-topics: []
- name: HealthData
title: HealthData
- name: Health Data
title: Health Data
description: Trackers related to selling/sharing health data
trackingType: HealthData
default-consent: 'off'
Expand Down Expand Up @@ -194,8 +194,8 @@ purposes:
display-order: 10
auth-level: REQUIRED
preference-topics: []
- name: SessionReplay
title: SessionReplay
- name: Session Replay
title: Session Replay
description: Session replay data flows and cookies (like FullStory)
trackingType: SessionReplay
default-consent: 'off'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"author": "Transcend Inc.",
"name": "@transcend-io/cli",
"description": "Small package containing useful typescript utilities.",
"version": "6.26.0",
"version": "6.27.0",
"homepage": "https://github.com/transcend-io/cli",
"repository": {
"type": "git",
Expand Down
28 changes: 17 additions & 11 deletions src/codecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1777,44 +1777,46 @@ export const AssessmentInput = t.intersection([
/** Type override */
export type AssessmentInput = t.TypeOf<typeof AssessmentInput>;

export const ConsentPreferenceTopicOptionValue = t.type({
export const PreferenceTopicOptionValueInput = t.type({
/** Title of option value */
title: t.string,
/** API slug */
slug: t.string,
});

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

export const ConsentPreferenceTopic = t.intersection([
export const PreferenceTopicInput = t.intersection([
t.type({
/** The type of the preference topic */
type: valuesOf(PreferenceTopicType),
/** The title of the preference topic */
title: t.string,
/** The description of the preference topic */
description: t.string,
/** The slug of the preference topic */
slug: t.string,
}),
t.partial({
/** Default value */
'default-configuration': t.string,
/** Whether the preference topic is shown in privacy center */
'show-in-privacy-center': t.boolean,
/** The options when type is single or multi select */
options: t.array(ConsentPreferenceTopicOptionValue),
options: t.array(t.string),
}),
]);

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

export const ConsentPurpose = t.intersection([
export const PurposeInput = t.intersection([
t.type({
/** Consent purpose slug */
trackingType: t.string,
'tracking-type': t.string,
/** The title of the tracking purpose that appears in Consent Management and Privacy Center UIs */
title: t.string,
/** The display name of this tracking purpose */
Expand All @@ -1834,7 +1836,7 @@ export const ConsentPurpose = t.intersection([
/** Whether purpose is show in consent manger */
'show-in-consent-manager': t.boolean,
/** The preference topics configured for the purpose */
'preference-topics': t.array(ConsentPreferenceTopic),
'preference-topics': t.array(PreferenceTopicInput),
/** Authentication level for purpose on privacy center */
'auth-level': valuesOf(PreferenceStoreAuthLevel),
/** Opt out signals that should instantly opt out of this purpose */
Expand All @@ -1845,7 +1847,7 @@ export const ConsentPurpose = t.intersection([
]);

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

export const TranscendInput = t.partial({
/**
Expand Down Expand Up @@ -1967,7 +1969,11 @@ export const TranscendInput = t.partial({
/**
* Consent and preference management purposes
*/
purposes: t.array(ConsentPurpose),
purposes: t.array(PurposeInput),
/**
* Preference management options
*/
'preference-options': t.array(PreferenceTopicOptionValueInput),
});

/** Type override */
Expand Down
7 changes: 7 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export const TR_PUSH_RESOURCE_SCOPE_MAP: {
ScopeName.ManageConsentManager,
ScopeName.ManagePreferenceStoreSettings,
],
[TranscendPullResource.PreferenceOptions]: [
ScopeName.ManagePreferenceStoreSettings,
],
};

/**
Expand Down Expand Up @@ -122,6 +125,9 @@ export const TR_PULL_RESOURCE_SCOPE_MAP: {
ScopeName.ViewConsentManager,
ScopeName.ViewPreferenceStoreSettings,
],
[TranscendPullResource.PreferenceOptions]: [
ScopeName.ViewPreferenceStoreSettings,
],
};

export const TR_YML_RESOURCE_TO_FIELD_NAME: Record<
Expand Down Expand Up @@ -159,4 +165,5 @@ export const TR_YML_RESOURCE_TO_FIELD_NAME: Record<
[TranscendPullResource.Assessments]: 'assessments',
[TranscendPullResource.AssessmentTemplates]: 'assessment-templates',
[TranscendPullResource.Purposes]: 'purposes',
[TranscendPullResource.PreferenceOptions]: 'preference-options',
};
1 change: 1 addition & 0 deletions src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export enum TranscendPullResource {
Assessments = 'assessments',
AssessmentTemplates = 'assessmentTemplates',
Purposes = 'purposes',
PreferenceOptions = 'preferenceOptions',
}

/**
Expand Down
55 changes: 55 additions & 0 deletions src/graphql/fetchAllPreferenceOptionValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { GraphQLClient } from 'graphql-request';
import { PREFERENCE_OPTION_VALUES } from './gqls';
import { makeGraphQLRequest } from './makeGraphQLRequest';

export interface PreferenceOptionValue {
/** ID of preference option value */
id: string;
/** Slug of preference option value */
slug: string;
/** Title of preference option value */
title: {
/** ID */
id: string;
/** Default message */
defaultMessage: string;
};
}

const PAGE_SIZE = 20;

/**
* Fetch all preference option values in the organization
*
* @param client - GraphQL client
* @returns All preference option values in the organization
*/
export async function fetchAllPreferenceOptionValues(
client: GraphQLClient,
): Promise<PreferenceOptionValue[]> {
const preferenceOptionValues: PreferenceOptionValue[] = [];
let offset = 0;

// Whether to continue looping
let shouldContinue = false;
do {
const {
preferenceOptionValues: { nodes },
// eslint-disable-next-line no-await-in-loop
} = await makeGraphQLRequest<{
/** Preference option values */
preferenceOptionValues: {
/** List */
nodes: PreferenceOptionValue[];
};
}>(client, PREFERENCE_OPTION_VALUES, {
first: PAGE_SIZE,
offset,
});
preferenceOptionValues.push(...nodes);
offset += PAGE_SIZE;
shouldContinue = nodes.length === PAGE_SIZE;
} while (shouldContinue);

return preferenceOptionValues.sort((a, b) => a.slug.localeCompare(b.slug));
}
41 changes: 41 additions & 0 deletions src/graphql/gqls/preferenceTopic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,44 @@ export const PREFERENCE_TOPICS = gql`
}
}
`;

export const CREATE_OR_UPDATE_PREFERENCE_TOPIC = gql`
mutation CreateOrUpdatePreferenceTopic(
$input: CreateOrUpdatePreferenceTopicInput!
) {
createOrUpdatePreferenceTopic(input: $input) {
preferenceTopic {
id
}
}
}
`;

export const CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES = gql`
mutation CreateOrUpdatePreferenceOptionValues(
$input: CreateOrUpdatePreferenceOptionValuesInput!
) {
createOrUpdatePreferenceOptionValues(input: $input) {
preferenceOptionValues {
id
slug
}
}
}
`;

export const PREFERENCE_OPTION_VALUES = gql`
query PreferenceOptionValues {
preferenceOptionValues {
clientMutationId
nodes {
id
title {
id
defaultMessage
}
slug
}
}
}
`;
22 changes: 22 additions & 0 deletions src/graphql/gqls/purpose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,25 @@ export const PURPOSES = gql`
}
}
`;

export const UPDATE_PURPOSE = gql`
mutation TranscendCliUpdatePurpose($input: UpdatePurposeInput!) {
updatePurpose(input: $input) {
clientMutationId
purpose {
id
}
}
}
`;

export const CREATE_PURPOSE = gql`
mutation TranscendCliCreatePurpose($input: CreatePurposeInput!) {
createPurpose(input: $input) {
clientMutationId
purpose {
id
}
}
}
`;
1 change: 1 addition & 0 deletions src/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './setResourceAttributes';
export * from './buildTranscendGraphQLClient';
export * from './retryRequestEnricher';
export * from './gqls';
export * from './syncPreferenceOptionValues';
export * from './fetchAllAssessmentTemplates';
export * from './fetchAllAssessments';
export * from './fetchPromptThreads';
Expand Down
33 changes: 26 additions & 7 deletions src/graphql/pullTranscendConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import {
AssessmentSectionInput,
AssessmentSectionQuestionInput,
RiskLogicInput,
ConsentPurpose,
PurposeInput,
PreferenceTopicOptionValueInput,
} from '../codecs';
import {
RequestAction,
Expand Down Expand Up @@ -84,6 +85,7 @@ import { fetchAllActionItemCollections } from './fetchAllActionItemCollections';
import { LanguageKey } from '@transcend-io/internationalization';
import { fetchPartitions } from './syncPartitions';
import { fetchAllAssessments } from './fetchAllAssessments';
import { fetchAllPreferenceOptionValues } from './fetchAllPreferenceOptionValues';
import { fetchAllAssessmentTemplates } from './fetchAllAssessmentTemplates';
import {
AssessmentNestedRule,
Expand Down Expand Up @@ -183,6 +185,7 @@ export async function pullTranscendConfiguration(
assessments,
assessmentTemplates,
purposes,
preferenceOptionValues,
] = await Promise.all([
// Grab all data subjects in the organization
resources.includes(TranscendPullResource.DataSilos) ||
Expand Down Expand Up @@ -335,6 +338,10 @@ export async function pullTranscendConfiguration(
resources.includes(TranscendPullResource.Purposes)
? fetchAllPurposesAndPreferences(client)
: [],
// Fetch preferenceOptionValues
resources.includes(TranscendPullResource.PreferenceOptions)
? fetchAllPreferenceOptionValues(client)
: [],
]);

const consentManagerTheme =
Expand Down Expand Up @@ -1357,11 +1364,11 @@ export async function pullTranscendConfiguration(
topics,
showInPrivacyCenter,
title,
}): ConsentPurpose => ({
}): PurposeInput => ({
name,
title,
description: description || undefined,
trackingType,
'tracking-type': trackingType,
'default-consent': defaultConsent,
configurable,
'show-in-consent-manager': showInConsentManager,
Expand All @@ -1374,22 +1381,21 @@ export async function pullTranscendConfiguration(
({
title,
type,
slug,
displayDescription,
defaultConfiguration,
showInPrivacyCenter,
preferenceOptionValues,
}) => ({
title: title.defaultMessage,
type,
slug,
description: displayDescription.defaultMessage,
'default-configuration': defaultConfiguration,
'show-in-privacy-center': showInPrivacyCenter,
...(preferenceOptionValues.length > 0
? {
options: preferenceOptionValues.map(({ title, slug }) => ({
title: title.defaultMessage,
slug,
})),
options: preferenceOptionValues.map(({ slug }) => slug),
}
: {}),
}),
Expand All @@ -1398,6 +1404,19 @@ export async function pullTranscendConfiguration(
);
}

// save preference options
if (
preferenceOptionValues.length > 0 &&
resources.includes(TranscendPullResource.PreferenceOptions)
) {
result['preference-options'] = preferenceOptionValues.map(
({ slug, title }): PreferenceTopicOptionValueInput => ({
slug,
title: title.defaultMessage,
}),
);
}

// save email templates
if (
dataSiloIds.length === 0 &&
Expand Down
Loading
Loading