Skip to content

Commit 2a35329

Browse files
authored
Merge pull request DSpace#1816 from 4Science/CST-6565
Cookie consent shows analytics usage also when GA is disabled
2 parents 06f3f8d + 16523c3 commit 2a35329

2 files changed

Lines changed: 115 additions & 7 deletions

File tree

src/app/shared/cookies/browser-klaro.service.spec.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,36 @@ import { AuthService } from '../../core/auth/auth.service';
1010
import { CookieService } from '../../core/services/cookie.service';
1111
import { getTestScheduler } from 'jasmine-marbles';
1212
import { MetadataValue } from '../../core/shared/metadata.models';
13-
import { cloneDeep } from 'lodash';
13+
import {clone, cloneDeep} from 'lodash';
14+
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
15+
import {createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$} from '../remote-data.utils';
16+
import { ConfigurationProperty } from '../../core/shared/configuration-property.model';
1417

1518
describe('BrowserKlaroService', () => {
19+
const trackingIdProp = 'google.analytics.key';
20+
const trackingIdTestValue = 'mock-tracking-id';
21+
const googleAnalytics = 'google-analytics';
1622
let translateService;
1723
let ePersonService;
1824
let authService;
1925
let cookieService;
2026

2127
let user;
2228
let service: BrowserKlaroService;
29+
let configurationDataService: ConfigurationDataService;
30+
const createConfigSuccessSpy = (...values: string[]) => jasmine.createSpyObj('configurationDataService', {
31+
findByPropertyName: createSuccessfulRemoteDataObject$({
32+
... new ConfigurationProperty(),
33+
name: trackingIdProp,
34+
values: values,
35+
}),
36+
});
2337

2438
let mockConfig;
2539
let appName;
2640
let purpose;
2741
let testKey;
42+
let findByPropertyName;
2843

2944
beforeEach(() => {
3045
user = new EPerson();
@@ -38,6 +53,8 @@ describe('BrowserKlaroService', () => {
3853
isAuthenticated: observableOf(true),
3954
getAuthenticatedUserFromStore: observableOf(user)
4055
});
56+
configurationDataService = createConfigSuccessSpy(trackingIdTestValue);
57+
findByPropertyName = configurationDataService.findByPropertyName;
4158
cookieService = jasmine.createSpyObj('cookieService', {
4259
get: '{%22token_item%22:true%2C%22impersonation%22:true%2C%22redirect%22:true%2C%22language%22:true%2C%22klaro%22:true%2C%22has_agreed_end_user%22:true%2C%22google-analytics%22:true}',
4360
set: () => {
@@ -63,6 +80,10 @@ describe('BrowserKlaroService', () => {
6380
{
6481
provide: CookieService,
6582
useValue: cookieService
83+
},
84+
{
85+
provide: ConfigurationDataService,
86+
useValue: configurationDataService
6687
}
6788
]
6889
});
@@ -83,6 +104,9 @@ describe('BrowserKlaroService', () => {
83104
services: [{
84105
name: appName,
85106
purposes: [purpose]
107+
},{
108+
name: googleAnalytics,
109+
purposes: [purpose]
86110
}],
87111

88112
};
@@ -233,4 +257,54 @@ describe('BrowserKlaroService', () => {
233257
expect(ePersonService.patch).not.toHaveBeenCalled();
234258
});
235259
});
260+
261+
describe('initialize google analytics configuration', () => {
262+
let GOOGLE_ANALYTICS_KEY;
263+
beforeEach(() => {
264+
GOOGLE_ANALYTICS_KEY = clone((service as any).GOOGLE_ANALYTICS_KEY);
265+
configurationDataService.findByPropertyName = findByPropertyName;
266+
spyOn((service as any), 'getUser$').and.returnValue(observableOf(user));
267+
translateService.get.and.returnValue(observableOf('loading...'));
268+
spyOn(service, 'addAppMessages');
269+
spyOn((service as any), 'initializeUser');
270+
spyOn(service, 'translateConfiguration');
271+
});
272+
it('should not filter googleAnalytics when servicesToHide are empty', () => {
273+
const filteredConfig = (service as any).filterConfigServices([]);
274+
expect(filteredConfig).toContain(jasmine.objectContaining({name: googleAnalytics}));
275+
});
276+
it('should filter services using names passed as servicesToHide', () => {
277+
const filteredConfig = (service as any).filterConfigServices([googleAnalytics]);
278+
expect(filteredConfig).not.toContain(jasmine.objectContaining({name: googleAnalytics}));
279+
});
280+
it('should have been initialized with googleAnalytics', () => {
281+
service.initialize();
282+
expect(service.klaroConfig.services).toContain(jasmine.objectContaining({name: googleAnalytics}));
283+
});
284+
it('should filter googleAnalytics when empty configuration is retrieved', () => {
285+
configurationDataService.findByPropertyName = jasmine.createSpy().withArgs(GOOGLE_ANALYTICS_KEY).and.returnValue(
286+
createSuccessfulRemoteDataObject$({
287+
... new ConfigurationProperty(),
288+
name: googleAnalytics,
289+
values: [],
290+
}));
291+
292+
service.initialize();
293+
expect(service.klaroConfig.services).not.toContain(jasmine.objectContaining({name: googleAnalytics}));
294+
});
295+
it('should filter googleAnalytics when an error occurs', () => {
296+
configurationDataService.findByPropertyName = jasmine.createSpy().withArgs(GOOGLE_ANALYTICS_KEY).and.returnValue(
297+
createFailedRemoteDataObject$('Erro while loading GA')
298+
);
299+
service.initialize();
300+
expect(service.klaroConfig.services).not.toContain(jasmine.objectContaining({name: googleAnalytics}));
301+
});
302+
it('should filter googleAnalytics when an invalid payload is retrieved', () => {
303+
configurationDataService.findByPropertyName = jasmine.createSpy().withArgs(GOOGLE_ANALYTICS_KEY).and.returnValue(
304+
createSuccessfulRemoteDataObject$(null)
305+
);
306+
service.initialize();
307+
expect(service.klaroConfig.services).not.toContain(jasmine.objectContaining({name: googleAnalytics}));
308+
});
309+
});
236310
});

src/app/shared/cookies/browser-klaro.service.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO
44
import { AuthService } from '../../core/auth/auth.service';
55
import { TranslateService } from '@ngx-translate/core';
66
import { environment } from '../../../environments/environment';
7-
import { switchMap, take } from 'rxjs/operators';
7+
import { map, switchMap, take } from 'rxjs/operators';
88
import { EPerson } from '../../core/eperson/models/eperson.model';
99
import { KlaroService } from './klaro.service';
10-
import { hasValue, isNotEmpty } from '../empty.util';
10+
import { hasValue, isEmpty, isNotEmpty } from '../empty.util';
1111
import { CookieService } from '../../core/services/cookie.service';
1212
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
1313
import { cloneDeep, debounce } from 'lodash';
1414
import { ANONYMOUS_STORAGE_NAME_KLARO, klaroConfiguration } from './klaro-configuration';
1515
import { Operation } from 'fast-json-patch';
16+
import { getFirstCompletedRemoteData} from '../../core/shared/operators';
17+
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
1618

1719
/**
1820
* Metadata field to store a user's cookie consent preferences in
@@ -38,23 +40,31 @@ const cookiePurposeMessagePrefix = 'cookies.consent.purpose.';
3840
* Update request debounce in ms
3941
*/
4042
const updateDebounce = 300;
43+
4144
/**
4245
* Browser implementation for the KlaroService, representing a service for handling Klaro consent preferences and UI
4346
*/
4447
@Injectable()
4548
export class BrowserKlaroService extends KlaroService {
49+
50+
private readonly GOOGLE_ANALYTICS_KEY = 'google.analytics.key';
51+
52+
private readonly GOOGLE_ANALYTICS_SERVICE_NAME = 'google-analytics';
53+
4654
/**
4755
* Initial Klaro configuration
4856
*/
49-
klaroConfig = klaroConfiguration;
57+
klaroConfig = cloneDeep(klaroConfiguration);
5058

5159
constructor(
5260
private translateService: TranslateService,
5361
private authService: AuthService,
5462
private ePersonService: EPersonDataService,
63+
private configService: ConfigurationDataService,
5564
private cookieService: CookieService) {
5665
super();
5766
}
67+
5868
/**
5969
* Initializes the service:
6070
* - Retrieves the current authenticated user
@@ -68,14 +78,25 @@ export class BrowserKlaroService extends KlaroService {
6878
this.klaroConfig.translations.en.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy';
6979
}
7080

81+
const servicesToHide$: Observable<string[]> = this.configService.findByPropertyName(this.GOOGLE_ANALYTICS_KEY).pipe(
82+
getFirstCompletedRemoteData(),
83+
map(remoteData => {
84+
if (!remoteData.hasSucceeded || !remoteData.payload || isEmpty(remoteData.payload.values)) {
85+
return [this.GOOGLE_ANALYTICS_SERVICE_NAME];
86+
} else {
87+
return [];
88+
}
89+
}),
90+
);
91+
7192
this.translateService.setDefaultLang(environment.defaultLanguage);
7293

7394
const user$: Observable<EPerson> = this.getUser$();
7495

7596
const translationServiceReady$ = this.translateService.get('loading.default').pipe(take(1));
7697

77-
observableCombineLatest([user$, translationServiceReady$])
78-
.subscribe(([user, translation]: [EPerson, string]) => {
98+
observableCombineLatest([user$, servicesToHide$, translationServiceReady$])
99+
.subscribe(([user, servicesToHide, _]: [EPerson, string[], string]) => {
79100
user = cloneDeep(user);
80101

81102
if (hasValue(user)) {
@@ -93,6 +114,9 @@ export class BrowserKlaroService extends KlaroService {
93114
* Show the configuration if the configuration has not been confirmed
94115
*/
95116
this.translateConfiguration();
117+
118+
this.klaroConfig.services = this.filterConfigServices(servicesToHide);
119+
96120
Klaro.setup(this.klaroConfig);
97121
});
98122
}
@@ -168,7 +192,10 @@ export class BrowserKlaroService extends KlaroService {
168192
*/
169193
addAppMessages() {
170194
this.klaroConfig.services.forEach((app) => {
171-
this.klaroConfig.translations.en[app.name] = { title: this.getTitleTranslation(app.name), description: this.getDescriptionTranslation(app.name) };
195+
this.klaroConfig.translations.en[app.name] = {
196+
title: this.getTitleTranslation(app.name),
197+
description: this.getDescriptionTranslation(app.name)
198+
};
172199
app.purposes.forEach((purpose) => {
173200
this.klaroConfig.translations.en.purposes[purpose] = this.getPurposeTranslation(purpose);
174201
});
@@ -257,4 +284,11 @@ export class BrowserKlaroService extends KlaroService {
257284
getStorageName(identifier: string) {
258285
return 'klaro-' + identifier;
259286
}
287+
288+
/**
289+
* remove the google analytics from the services
290+
*/
291+
private filterConfigServices(servicesToHide: string[]): Pick<typeof klaroConfiguration, 'services'>[] {
292+
return this.klaroConfig.services.filter(service => !servicesToHide.some(name => name === service.name));
293+
}
260294
}

0 commit comments

Comments
 (0)