Skip to content

Commit

Permalink
option to pass ConsentState explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
vlkerag committed Jan 10, 2024
1 parent 3ba5a9a commit 33e70a3
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 26 deletions.
45 changes: 41 additions & 4 deletions src/cmpCookies.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import { getCookie, setCookie, setSessionCookie } from '@guardian/libs';
import type { ConsentUseCases } from './types/consentUseCases';
import { hasConsentForUseCase } from './hasConsentForUseCase';
import { ConsentState } from './types';
import { onConsent } from './onConsent';

//TODO: Write wrappers for the other cookie functions in @guardian/libs
//TODO?: Write wrappers for the other cookie functions in @guardian/libs

export const cmpGetCookie = async({ useCase, name, shouldMemoize, }: {
useCase: ConsentUseCases,
name: string;
shouldMemoize?: boolean | undefined;
}): Promise<string | null> =>
{
const consentState = await onConsent();
return(cmpGetCookieWithConsentState({useCase, consentState, name, shouldMemoize}))
}

export const cmpGetCookieWithConsentState = ({ useCase, consentState, name, shouldMemoize}: {
useCase: ConsentUseCases,
consentState: ConsentState,
name: string;
shouldMemoize?: boolean | undefined;
}): string | null =>
{
console.log('in cmpGetCookie');

if(await hasConsentForUseCase(useCase))
if(hasConsentForUseCase(useCase, consentState))
{
return getCookie({name: name, shouldMemoize: shouldMemoize})
}
Expand All @@ -30,10 +43,23 @@ export const cmpSetCookie = async ({ useCase, name, value, daysToLive, isCrossSu
daysToLive?: number | undefined;
isCrossSubdomain?: boolean | undefined;
}): Promise<void> =>
{
const consentState = await onConsent();
return(cmpSetCookieWithConsentState({useCase, consentState, name, value, daysToLive, isCrossSubdomain}))
}

export const cmpSetCookieWithConsentState = ({ useCase, consentState, name, value, daysToLive, isCrossSubdomain, }: {
useCase: ConsentUseCases,
consentState: ConsentState,
name: string;
value: string;
daysToLive?: number | undefined;
isCrossSubdomain?: boolean | undefined;
}): void =>
{
console.log('in cmpSetCookie');

if(await hasConsentForUseCase(useCase))
if(hasConsentForUseCase(useCase, consentState))
{
setCookie({name:name, value:value, daysToLive:daysToLive, isCrossSubdomain:isCrossSubdomain})
}
Expand All @@ -48,10 +74,21 @@ export const cmpSetSessionCookie = async ({ useCase, name, value }: {
name: string;
value: string;
}): Promise<void> =>
{
const consentState = await onConsent();
return(cmpSetSessionCookieWithConsentState({useCase, consentState, name, value}))
};

export const cmpSetSessionCookieWithConsentState= async ({ useCase, consentState, name, value }: {
useCase: ConsentUseCases,
consentState: ConsentState,
name: string;
value: string;
}): Promise<void> =>
{
console.log('in cmpSetSessionCookie');

if(await hasConsentForUseCase(useCase))
if(hasConsentForUseCase(useCase, consentState))
{
setSessionCookie({name:name, value:value})
}
Expand Down
70 changes: 62 additions & 8 deletions src/cmpStorage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { storage as libsStorage } from '@guardian/libs';
import type { ConsentUseCases } from './types/consentUseCases';
import { hasConsentForUseCase } from './hasConsentForUseCase';
import { ConsentState } from './types';
import { onConsent } from '.';

export const storageOptions = [
"localStorage",
Expand Down Expand Up @@ -31,11 +33,24 @@ class StorageFactory {
/**
* Retrieve an item from storage.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param key - the name of the item
*/
async get(useCase: ConsentUseCases, key: string): Promise<unknown> {
const consentState = await onConsent();
return(this.getWithConsentState(useCase, consentState, key))
}

/**
* Retrieve an item from storage.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param consentState - the ConsentState to check if consent for the useCase has been given
* @param key - the name of the item
*/
getWithConsentState(useCase: ConsentUseCases, consentState: ConsentState, key: string): unknown {
console.log('in cmp get storage');
if(await hasConsentForUseCase(useCase))
if(hasConsentForUseCase(useCase, consentState))
{
switch(this.#storageHandler) {
case 'localStorage': {
Expand All @@ -55,16 +70,31 @@ class StorageFactory {
}
}

/**
/**
* Save a value to storage.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param key - the name of the item
* @param value - the data to save
* @param expires - optional date on which this data will expire
*/
async set(useCase:ConsentUseCases, key: string, value: unknown, expires?: string | number | Date): Promise<void> {
const consentState = await onConsent();
return(this.setWithConsentState(useCase, consentState, key, value, expires))
}

/**
* Save a value to storage.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param consentState - the ConsentState to check if consent for the useCase has been given
* @param key - the name of the item
* @param value - the data to save
* @param expires - optional date on which this data will expire
*/
setWithConsentState(useCase:ConsentUseCases, consentState: ConsentState, key: string, value: unknown, expires?: string | number | Date): void {
console.log('in cmp set storage');
if(await hasConsentForUseCase(useCase))
if(hasConsentForUseCase(useCase, consentState))
{
switch(this.#storageHandler) {
case 'localStorage': return libsStorage.local.set(key, value, expires)
Expand Down Expand Up @@ -112,10 +142,23 @@ class StorageFactory {
/**
* Retrieve an item from storage in its raw state.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param key - the name of the item
*/
async getRaw(useCase: ConsentUseCases, key: string): Promise<string | null> {
if(await hasConsentForUseCase(useCase))
const consentState = await onConsent();
return(this.getRawWithConsentState(useCase,consentState,key))
}

/**
* Retrieve an item from storage in its raw state.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param consentState - the ConsentState to check if consent for the useCase has been given
* @param key - the name of the item
*/
getRawWithConsentState(useCase: ConsentUseCases, consentState: ConsentState, key: string): string | null {
if(hasConsentForUseCase(useCase, consentState))
{
switch(this.#storageHandler) {
case 'localStorage': {
Expand All @@ -136,11 +179,25 @@ class StorageFactory {
/**
* Save a raw value to storage.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param key - the name of the item
* @param value - the data to save
*/
async setRaw(useCase: ConsentUseCases, key: string, value: string): Promise<void> {
if(await hasConsentForUseCase(useCase))
const consentState = await onConsent();
return(this.setRawWithConsentState(useCase, consentState, key, value))
}

/**
* Save a raw value to storage.
*
* @param useCase - the ConsentUseCase for which to get the data
* @param consentState - the ConsentState to check if consent for the useCase has been given
* @param key - the name of the item
* @param value - the data to save
*/
setRawWithConsentState(useCase: ConsentUseCases, consentState: ConsentState, key: string, value: string): void {
if(hasConsentForUseCase(useCase, consentState))
{
switch(this.#storageHandler) {
case 'localStorage': return libsStorage.local.setRaw(key, value)
Expand Down Expand Up @@ -180,6 +237,3 @@ export const storage = new (class {
}
})();


//await cmpGetLocalStorageItem("Targeted advertising", "dep")
//await cmpGetLocalStorageItem("invalid", "dep")
15 changes: 4 additions & 11 deletions src/hasConsentForUseCase.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { onConsentChange } from './onConsentChange';
import type { Callback, ConsentState } from './types';
import type { TCFv2ConsentState } from './types/tcfv2';
import { hasConsentForUseCase } from './hasConsentForUseCase';
import { ConsentState } from './types';

//TODO: add tests for all use-cases

Expand Down Expand Up @@ -41,18 +40,14 @@ const tcfv2ConsentStateNoConsent: TCFv2ConsentState = {
tcString: 'YAAA',
};

const mockOnConsentChange = (consentState: ConsentState) =>
(onConsentChange as jest.Mock).mockImplementation((cb: Callback) => cb(consentState));

describe('cmpStorage.hasConsentForUseCase returns the expected consent', () => {
test('Targeted advertising has consent when canTarget is true', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: true,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const hasConsent = await hasConsentForUseCase('Targeted advertising');
const hasConsent = hasConsentForUseCase('Targeted advertising', consentState);
expect(hasConsent).toEqual(true);
});
test('Targeted advertising has no consent when canTarget is false', async () => {
Expand All @@ -61,8 +56,7 @@ describe('cmpStorage.hasConsentForUseCase returns the expected consent', () => {
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const hasConsent = await hasConsentForUseCase('Targeted advertising');
const hasConsent = hasConsentForUseCase('Targeted advertising', consentState);
expect(hasConsent).toEqual(false);
});
test('Essential has consent even when ConsentState has no consents', async () => {
Expand All @@ -71,8 +65,7 @@ describe('cmpStorage.hasConsentForUseCase returns the expected consent', () => {
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const hasConsent = await hasConsentForUseCase('Essential');
const hasConsent = hasConsentForUseCase('Essential', consentState);
expect(hasConsent).toEqual(true);
});
});
6 changes: 3 additions & 3 deletions src/hasConsentForUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { onConsent } from './onConsent';
//import { onConsent } from './onConsent';
import { ConsentState } from './types';
import type { ConsentUseCases } from './types/consentUseCases';

export const hasConsentForUseCase = async (useCase: ConsentUseCases): Promise<boolean> =>
export const hasConsentForUseCase = (useCase: ConsentUseCases, consentState: ConsentState): boolean =>
{
const consentState = await onConsent();

/*console.log(`consentState.tcfv2?.consents['1']: ${consentState.tcfv2?.consents['1']}`);
console.log(`consentState.tcfv2?.consents['2']: ${consentState.tcfv2?.consents['2']}`);
Expand Down

1 comment on commit 33e70a3

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 80.35% 278/346
🟡 Branches 67.76% 103/152
🟡 Functions 78.02% 71/91
🟡 Lines 79.82% 269/337

Test suite run success

339 tests passing in 18 suites.

Report generated by 🧪jest coverage report action from 33e70a3

Please sign in to comment.