Skip to content
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
7 changes: 6 additions & 1 deletion modules/cmpapi/src/cmpapi/command/GetFieldCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ export class GetFieldCommand extends Command {
let sectionName = parts[0];
let fieldName = parts[1];

let fieldValue = this.cmpApiContext.gppModel.getFieldValue(sectionName, fieldName);
let fieldValue = null;
// Since TCF 2.2 no fields are allowed to be called directly. Data always needs to be retrieved using the
// AddEventListener callback
if (this.parameter != "tcfeuv2") {
fieldValue = this.cmpApiContext.gppModel.getFieldValue(sectionName, fieldName);
}
this.invokeCallback(fieldValue);
}
}
8 changes: 6 additions & 2 deletions modules/cmpapi/src/cmpapi/command/GetSectionCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ export class GetSectionCommand extends Command {
}

let section = null;
if (this.cmpApiContext.gppModel.hasSection(this.parameter)) {
section = this.cmpApiContext.gppModel.getSection(this.parameter);
// Since TCF 2.2 no fields are allowed to be called directly. Data always needs to be retrieved using the
// AddEventListener callback
if (this.parameter != "tcfeuv2") {
if (this.cmpApiContext.gppModel.hasSection(this.parameter)) {
section = this.cmpApiContext.gppModel.getSection(this.parameter);
}
}
this.invokeCallback(section);
}
Expand Down
37 changes: 31 additions & 6 deletions modules/cmpapi/src/encoder/section/TcfEuV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ export class TcfEuV2 extends AbstractLazilyEncodableSection {
if (segments.length >= 2) {
encodedSegments.push(segments[1].encode());
}
if (segments.length >= 3) {
encodedSegments.push(segments[3].encode());
}
Comment on lines +101 to +103

Choose a reason for hiding this comment

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

Nit: Technically the ENUM value for VendorsDisclosed is 1 and PublisherPurposes is 3...so shouldn't the segments switch order? I know their first field indicates their segment type, and it doesn't REALLY matter, so I'm fine with just doing it this way. I mean, it's been in the wrong order for this long right?

} else {
if (segments.length >= 2) {
encodedSegments.push(segments[2].encode());
Expand All @@ -113,13 +116,35 @@ export class TcfEuV2 extends AbstractLazilyEncodableSection {

//Overriden
public setFieldValue(fieldName: string, value: any): void {
super.setFieldValue(fieldName, value);
//
// special handling for exceptions based on TCF policy and specification
//
// purpose 1, 3 - 6 legitimate interest is always false
if (fieldName === TcfEuV2Field.PURPOSE_LEGITIMATE_INTERESTS) {
value[0] = false;
value[2] = value[3] = value[4] = value[5] = false;
}

if (fieldName !== TcfEuV2Field.CREATED && fieldName !== TcfEuV2Field.LAST_UPDATED) {
let date = new Date();
// create and last update will need to stay in sync
if (fieldName === TcfEuV2Field.CREATED || fieldName === TcfEuV2Field.LAST_UPDATED) {
if (fieldName === TcfEuV2Field.CREATED) {
super.setFieldValue(TcfEuV2Field.LAST_UPDATED, value);
} else {
super.setFieldValue(TcfEuV2Field.CREATED, value);
}
} else
// always update the date stamp with a change occurs
this.updateDateStamp();

super.setFieldValue(TcfEuV2Field.CREATED, date);
super.setFieldValue(TcfEuV2Field.LAST_UPDATED, date);
}
// update the actual given fields
super.setFieldValue(fieldName, value);
}

// update last_update and create time stamps when a change occurs
private updateDateStamp(): void {
const date = new Date();
const utcDate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
super.setFieldValue(TcfEuV2Field.CREATED, utcDate);
super.setFieldValue(TcfEuV2Field.LAST_UPDATED, utcDate);
Comment on lines +144 to +148

Choose a reason for hiding this comment

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

🎉 THANK YOU! Only using Year, Month, Date is the correct implementation.

}
}
2 changes: 1 addition & 1 deletion modules/cmpapi/src/encoder/segment/TcfEuV2CoreSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class TcfEuV2CoreSegment extends AbstractLazilyEncodableSegment<Encodable
fields.put(TcfEuV2Field.CONSENT_SCREEN.toString(), new EncodableFixedInteger(6, 0));
fields.put(TcfEuV2Field.CONSENT_LANGUAGE.toString(), new EncodableFixedString(2, "EN"));
fields.put(TcfEuV2Field.VENDOR_LIST_VERSION.toString(), new EncodableFixedInteger(12, 0));
fields.put(TcfEuV2Field.POLICY_VERSION.toString(), new EncodableFixedInteger(6, 2));
fields.put(TcfEuV2Field.POLICY_VERSION.toString(), new EncodableFixedInteger(6, 5));
fields.put(TcfEuV2Field.IS_SERVICE_SPECIFIC.toString(), new EncodableBoolean(false));
fields.put(TcfEuV2Field.USE_NON_STANDARD_STACKS.toString(), new EncodableBoolean(false));
fields.put(
Expand Down
2 changes: 2 additions & 0 deletions modules/cmpapi/src/gvl/gvlmodel/ConsentLanguages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class ConsentLanguages {
'FR',
'GL',
'HE',
'HI',
'HR',
'HU',
'ID',
Expand Down Expand Up @@ -49,6 +50,7 @@ export class ConsentLanguages {
'UK',
'VI',
'ZH',
'ZH-HANT',
]);

public has(key: string): boolean {
Expand Down
2 changes: 1 addition & 1 deletion modules/cmpapi/test/GVLV2_2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe("GVL", (): void => {

it("number of language translations should match", (): void => {
const langSet = new ConsentLanguages();
expect(langSet.size).equal(49);
expect(langSet.size).equal(51);
expect(langSet.has("VI")).equal(true);
});

Expand Down
66 changes: 55 additions & 11 deletions modules/cmpapi/test/GppModel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ describe("manifest.GppModel", (): void => {
expect(gppModel.hasSection("ustn")).to.eql(false);

gppModel.setFieldValue("tcfeuv2", "Version", 2);
gppModel.setFieldValue("tcfeuv2", "CmpId", 880);
gppModel.setFieldValue("tcfcav1", "Version", 1);
gppModel.setFieldValue("tcfeuv2", "Created", utcDateTime);
gppModel.setFieldValue("tcfeuv2", "LastUpdated", utcDateTime);
gppModel.setFieldValue("tcfcav1", "Version", 1);
gppModel.setFieldValue("tcfcav1", "Created", utcDateTime);
gppModel.setFieldValue("tcfcav1", "LastUpdated", utcDateTime);
gppModel.setFieldValue("uspv1", "Version", 1);
Expand Down Expand Up @@ -106,7 +107,7 @@ describe("manifest.GppModel", (): void => {

let gppString = gppModel.encode();
expect(gppString).to.eql(
"DBACOdM~CPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA~BPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAA~1---~BAAAAAAAAABA.QA~BAAAAABA.QA~BAAAABA~BAAAAEA.QA~BAAAAAQA~BAAAAAEA.QA~BAAAAABA~BAAAAABA.QA~BAAAAAABAA.QA~BAAAAAQA.QA~BAAAAAABAA.QA~BAAAAAQA.QA~BAAAAAQA.QA~BAAAAABA.QA~BAAAAAAAQA.QA~BAAAAAQA.QA"
"DBACOdM~CPSG_8APSG_8ANwAAAENAAFAAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA~BPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAA~1---~BAAAAAAAAABA.QA~BAAAAABA.QA~BAAAABA~BAAAAEA.QA~BAAAAAQA~BAAAAAEA.QA~BAAAAABA~BAAAAABA.QA~BAAAAAABAA.QA~BAAAAAQA.QA~BAAAAAABAA.QA~BAAAAAQA.QA~BAAAAAQA.QA~BAAAAABA.QA~BAAAAAAAQA.QA~BAAAAAQA.QA"
);
});

Expand Down Expand Up @@ -145,20 +146,25 @@ describe("manifest.GppModel", (): void => {
gppModel.setFieldValue("tcfeuv2", "ConsentLanguage", "EN");
gppModel.setFieldValue("tcfeuv2", "VendorListVersion", 48);
gppModel.setFieldValue("tcfeuv2", "PolicyVersion", 2);
gppModel.setFieldValue("tcfeuv2", "IsServiceSpecific", false);
gppModel.setFieldValue("tcfeuv2", "IsServiceSpecific", true);
gppModel.setFieldValue("tcfeuv2", "UseNonStandardStacks", false);
gppModel.setFieldValue("tcfeuv2", "PurposeOneTreatment", false);
gppModel.setFieldValue("tcfeuv2", "PublisherCountryCode", "AA");
gppModel.setFieldValue("tcfeuv2", "PublisherCountryCode", "DE");
gppModel.setFieldValue("tcfeuv2", "Created", utcDateTime);
gppModel.setFieldValue("tcfeuv2", "LastUpdated", utcDateTime);

// set a few vendors
gppModel.setFieldValue("tcfeuv2", "VendorConsents", [1, 2, 3, 4]);
gppModel.setFieldValue("tcfeuv2", "VendorLegitimateInterests", []);
gppModel.setFieldValue("tcfeuv2", "VendorsDisclosed", [1, 2, 3, 4, 5, 100, 404]);

expect(gppModel.getSectionIds()).to.eql([2]);
expect(gppModel.hasSection("uspv1")).to.eql(false);
expect(gppModel.hasSection("tcfeuv2")).to.eql(true);
expect(gppModel.hasSection("tcfcav1")).to.eql(false);

let gppString = gppModel.encode();
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8ANwAAAENAwCAAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA");
expect(gppString).to.eql("DBABMA~CQSbk4AQSbk4ANwAAAENAwCgAAAAAAAAAAYgACPAAAAA.YAAAAAAAAAAA.IDKQA4AAgAKAGQAygAAA");

expect(gppString.split("~").length).to.eql(2);

Expand Down Expand Up @@ -665,7 +671,7 @@ describe("manifest.GppModel", (): void => {
gppModel.setFieldValue("tcfeuv2", "LastUpdated", utcDateTime);

let gppString = gppModel.encode();
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAAOAAAABAAAAA.QAAA.IAAA");
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8AAAAAAENAAFAAAAAAAAAAAAAAOAAAABAAAAA.QAAA.IAAA");
});

it("should encode tcfeuv2 vendor consents [29]", (): void => {
Expand All @@ -675,7 +681,7 @@ describe("manifest.GppModel", (): void => {
gppModel.setFieldValue("tcfeuv2", "LastUpdated", utcDateTime);

let gppString = gppModel.encode();
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAAOwAQAOgAAAA.QAAA.IAAA");
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8AAAAAAENAAFAAAAAAAAAAAAAAOwAQAOgAAAA.QAAA.IAAA");
});

it("should encode tcfeuv2 vendor consents [1, 173, 722]", (): void => {
Expand All @@ -685,7 +691,7 @@ describe("manifest.GppModel", (): void => {
gppModel.setFieldValue("tcfeuv2", "LastUpdated", utcDateTime);

let gppString = gppModel.encode();
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAFpQAwAAgCtAWkAAAAAAA.QAAA.IAAA");
expect(gppString).to.eql("DBABMA~CPSG_8APSG_8AAAAAAENAAFAAAAAAAAAAAAAFpQAwAAgCtAWkAAAAAAA.QAAA.IAAA");
});

it("should decode tcfeuv2 vendor consents [28]", (): void => {
Expand Down Expand Up @@ -764,16 +770,29 @@ describe("manifest.GppModel", (): void => {
it("should decode and encode consistently", (): void => {
let fromObjectModel = new GppModel();
fromObjectModel.setFieldValue("tcfeuv2", "PurposeConsents", [
true,
false,
true,
true,
false,
false,
true,
true,
true,
true,
true,
]);
fromObjectModel.setFieldValue("tcfeuv2", "PurposeLegitimateInterests", [
true,
true,
false,
false,
true,
true,
false,
false,
false,
false,
]);
fromObjectModel.setFieldValue("tcfeuv2", "VendorConsents", [32, 128, 81, 210, 755, 21, 173, 238]);

Expand All @@ -783,14 +802,32 @@ describe("manifest.GppModel", (): void => {

expect(decodedModel.getFieldValue("tcfeuv2", "PurposeConsents")).to.eql([
true,
false,
true,
true,
false,
false,
true,
true,
true,
true,
true,
true,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
]);
expect(decodedModel.getFieldValue("tcfeuv2", "PurposeLegitimateInterests")).to.eql([
false,
true,
false,
false,
Expand All @@ -806,8 +843,15 @@ describe("manifest.GppModel", (): void => {
false,
false,
false,
]);

false,
false,
false,
false,
false,
false,
false,
false
])
expect(decodedModel.getFieldValue("tcfeuv2", "VendorConsents")).to.eql([21, 32, 81, 128, 173, 210, 238, 755]);
});

Expand Down
11 changes: 6 additions & 5 deletions modules/cmpapi/test/encoder/section/TcfEuV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ describe("manifest.section.TcfEuV2", (): void => {
let tcfEuV2 = new TcfEuV2();
tcfEuV2.setFieldValue(TcfEuV2Field.CREATED, new Date("2022-01-01T00:00:00Z"));
tcfEuV2.setFieldValue(TcfEuV2Field.LAST_UPDATED, new Date("2022-01-01T00:00:00Z"));
expect(tcfEuV2.encode()).to.eql("CPSG_8APSG_8AAAAAAENAACAAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA");
expect(tcfEuV2.encode()).to.eql("CPSG_8APSG_8AAAAAAENAAFAAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA");
});

it("encode with service specific", (): void => {
let tcfEuV2 = new TcfEuV2();
tcfEuV2.setFieldValue("IsServiceSpecific", true);
tcfEuV2.setFieldValue("Created", new Date("2022-01-01T00:00:00Z"));
tcfEuV2.setFieldValue("LastUpdated", new Date("2022-01-01T00:00:00Z"));
expect(tcfEuV2.encode()).to.eql("CPSG_8APSG_8AAAAAAENAACgAAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAAA");
tcfEuV2.setFieldValue("LastUpdated", new Date("2022-02-01T00:00:00Z"));
expect(tcfEuV2.encode()).to.eql("CPTtLAAPTtLAAAAAAAENAAFgAAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAAA.IAAA");
});

it("decode defaults", (): void => {
Expand Down Expand Up @@ -165,7 +165,7 @@ describe("manifest.section.TcfEuV2", (): void => {
});

it("decode service specific", (): void => {
let tcfEuV2 = new TcfEuV2("CPSG_8APSG_8AAAAAAENAACgAAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAAA");
let tcfEuV2 = new TcfEuV2("CPSG_8APSG_8AAAAAAENAACgAAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAAA.IAAA");
expect(tcfEuV2.getFieldValue("Version")).to.eql(2);
expect(tcfEuV2.getFieldValue("Created")).to.eql(new Date("2022-01-01T00:00:00Z"));
expect(tcfEuV2.getFieldValue("LastUpdated")).to.eql(new Date("2022-01-01T00:00:00Z"));
Expand Down Expand Up @@ -557,7 +557,7 @@ describe("manifest.section.TcfEuV2", (): void => {
});

it("decode 4", (): void => {
let tcfEuV2 = new TcfEuV2("COv_eg6Ov_eg6AOADBENAaCgAP_AAH_AACiQAVEUQQoAIQAqIoghAAQgAA.YAAAAAAAAAAAAAAAAAA");
let tcfEuV2 = new TcfEuV2("COv_eg6Ov_eg6AOADBENAaCgAP_AAH_AACiQAVEUQQoAIQAqIoghAAQgAA.YAAAAAAAAAAAAAAAAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g");

expect(tcfEuV2.getFieldValue("Version")).to.eql(2);
expect(tcfEuV2.getFieldValue("CmpId")).to.eql(14);
Expand Down Expand Up @@ -642,6 +642,7 @@ describe("manifest.section.TcfEuV2", (): void => {

expect(tcfEuV2.getFieldValue("VendorConsents")).to.eql([2, 6, 8, 12, 18, 23, 25, 37, 42]);
expect(tcfEuV2.getFieldValue("VendorLegitimateInterests")).to.eql([2, 6, 8, 12, 18, 23, 37, 42]);
expect(tcfEuV2.getFieldValue("VendorsDisclosed").length).to.eql(434);
});

it("should throw Error on garbage 1", (): void => {
Expand Down