diff --git a/README.md b/README.md index 4594727..eadc14b 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,21 @@ __gpp("removeEventListener", callback?, parameter?) | ustn | 22 | GpcSegmentType | 2 bit int. Value is 1 | | ustn | 22 | GpcSegmentIncluded | Boolean. Default is true | | ustn | 22 | Gpc | Boolean | +| usmn | 23 | Version | 6 bit int. Value is 1 | +| usmn | 23 | ProcessingNotice | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | SaleOptOutNotice | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | TargetedAdvertisingOptOutNotice | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | SaleOptOut | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | TargetedAdvertisingOptOut | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | SensitiveDataProcessing | 2 bit int array of size 8. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | KnownChildSensitiveDataConsents | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | AdditionalDataProcessingConsent | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | MspaCoveredTransaction | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | MspaOptOutOptionMode | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | MspaServiceProviderMode | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | GpcSegmentType | 2 bit int. Value is 1 | +| usmn | 23 | GpcSegmentIncluded | Boolean. Default is true | +| usmn | 23 | Gpc | Boolean | ## Example Usage / Encoder / Decoder diff --git a/modules/cmpapi/.mocharc.json b/modules/cmpapi/.mocharc.json index 4e54a9a..851e047 100644 --- a/modules/cmpapi/.mocharc.json +++ b/modules/cmpapi/.mocharc.json @@ -33,6 +33,7 @@ "./test/encoder/section/UsDe.test.ts", "./test/encoder/section/UsFl.test.ts", "./test/encoder/section/UsIa.test.ts", + "./test/encoder/section/UsMn.test.ts", "./test/encoder/section/UsMt.test.ts", "./test/encoder/section/UsNat.test.ts", "./test/encoder/section/UsNe.test.ts", diff --git a/modules/cmpapi/README.md b/modules/cmpapi/README.md index 66edb1c..af914e7 100644 --- a/modules/cmpapi/README.md +++ b/modules/cmpapi/README.md @@ -378,6 +378,21 @@ __gpp("removeEventListener", callback?, parameter?) | ustn | 22 | GpcSegmentType | 2 bit int. Value is 1 | | ustn | 22 | GpcSegmentIncluded | Boolean. Default is true | | ustn | 22 | Gpc | Boolean | +| usmn | 23 | Version | 6 bit int. Value is 1 | +| usmn | 23 | ProcessingNotice | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | SaleOptOutNotice | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | TargetedAdvertisingOptOutNotice | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | SaleOptOut | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | TargetedAdvertisingOptOut | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | SensitiveDataProcessing | 2 bit int array of size 8. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | KnownChildSensitiveDataConsents | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | AdditionalDataProcessingConsent | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | MspaCoveredTransaction | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | MspaOptOutOptionMode | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | MspaServiceProviderMode | 2 bit int. 0=Not applicable, 1=Yes, 2=No | +| usmn | 23 | GpcSegmentType | 2 bit int. Value is 1 | +| usmn | 23 | GpcSegmentIncluded | Boolean. Default is true | +| usmn | 23 | Gpc | Boolean | ## Example Usage / Encoder / Decoder diff --git a/modules/cmpapi/src/encoder/GppModel.ts b/modules/cmpapi/src/encoder/GppModel.ts index 387783a..20b105f 100644 --- a/modules/cmpapi/src/encoder/GppModel.ts +++ b/modules/cmpapi/src/encoder/GppModel.ts @@ -23,6 +23,7 @@ import { UsNe } from "./section/UsNe.js"; import { UsNh } from "./section/UsNh.js"; import { UsNj } from "./section/UsNj.js"; import { UsTn } from "./section/UsTn.js"; +import { UsMn } from "./section/UsMn.js"; export class GppModel { private sections = new Map(); @@ -103,6 +104,9 @@ export class GppModel { } else if (sectionName === UsTn.NAME) { section = new UsTn(); this.sections.set(UsTn.NAME, section); + } else if (sectionName === UsMn.NAME) { + section = new UsMn(); + this.sections.set(UsMn.NAME, section); } } else { section = this.sections.get(sectionName); @@ -335,6 +339,9 @@ export class GppModel { } else if (sectionIds[i] === UsTn.ID) { let section = new UsTn(encodedSections[i + 1]); sections.set(UsTn.NAME, section); + } else if (sectionIds[i] === UsMn.ID) { + let section = new UsMn(encodedSections[i + 1]); + sections.set(UsMn.NAME, section); } } } @@ -440,6 +447,9 @@ export class GppModel { } else if (sectionName === UsTn.NAME) { section = new UsTn(); this.sections.set(UsTn.NAME, section); + } else if (sectionName === UsMn.NAME) { + section = new UsMn(); + this.sections.set(UsMn.NAME, section); } } else { section = this.sections.get(sectionName); diff --git a/modules/cmpapi/src/encoder/field/UsMnField.ts b/modules/cmpapi/src/encoder/field/UsMnField.ts new file mode 100644 index 0000000..ba30ed3 --- /dev/null +++ b/modules/cmpapi/src/encoder/field/UsMnField.ts @@ -0,0 +1,35 @@ +export enum UsMnField { + VERSION = "Version", + PROCESSING_NOTICE = "ProcessingNotice", + SALE_OPT_OUT_NOTICE = "SaleOptOutNotice", + TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice", + SALE_OPT_OUT = "SaleOptOut", + TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut", + SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing", + KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents", + ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent", + MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction", + MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode", + MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode", + + GPC_SEGMENT_TYPE = "GpcSegmentType", + GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded", + GPC = "Gpc", +} + +export const UsMn_CORE_SEGMENT_FIELD_NAMES = [ + UsMnField.VERSION, + UsMnField.PROCESSING_NOTICE, + UsMnField.SALE_OPT_OUT_NOTICE, + UsMnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE, + UsMnField.SALE_OPT_OUT, + UsMnField.TARGETED_ADVERTISING_OPT_OUT, + UsMnField.SENSITIVE_DATA_PROCESSING, + UsMnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS, + UsMnField.ADDITIONAL_DATA_PROCESSING_CONSENT, + UsMnField.MSPA_COVERED_TRANSACTION, + UsMnField.MSPA_OPT_OUT_OPTION_MODE, + UsMnField.MSPA_SERVICE_PROVIDER_MODE, +]; + +export const UsMn_GPC_SEGMENT_FIELD_NAMES = [UsMnField.GPC_SEGMENT_TYPE, UsMnField.GPC]; diff --git a/modules/cmpapi/src/encoder/field/index.ts b/modules/cmpapi/src/encoder/field/index.ts index e545d20..8d58942 100644 --- a/modules/cmpapi/src/encoder/field/index.ts +++ b/modules/cmpapi/src/encoder/field/index.ts @@ -10,6 +10,7 @@ export * from "./UsCtField.js"; export * from "./UsDeField.js"; export * from "./UsFlField.js"; export * from "./UsIaField.js"; +export * from "./UsMnField.js"; export * from "./UsMtField.js"; export * from "./UsNatField.js"; export * from "./UsNeField.js"; diff --git a/modules/cmpapi/src/encoder/section/Sections.ts b/modules/cmpapi/src/encoder/section/Sections.ts index 782661d..17e908e 100644 --- a/modules/cmpapi/src/encoder/section/Sections.ts +++ b/modules/cmpapi/src/encoder/section/Sections.ts @@ -17,6 +17,7 @@ import { UsNe } from "./UsNe.js"; import { UsNh } from "./UsNh.js"; import { UsNj } from "./UsNj.js"; import { UsTn } from "./UsTn.js"; +import { UsMn } from "./UsMn.js"; export class Sections { public static SECTION_ID_NAME_MAP = new Map([ @@ -39,6 +40,7 @@ export class Sections { [UsNh.ID, UsNh.NAME], [UsNj.ID, UsNj.NAME], [UsTn.ID, UsTn.NAME], + [UsMn.ID, UsMn.NAME], ]); public static SECTION_ORDER = [ TcfEuV2.NAME, @@ -60,5 +62,6 @@ export class Sections { UsNh.NAME, UsNj.NAME, UsTn.NAME, + UsMn.NAME ]; } diff --git a/modules/cmpapi/src/encoder/section/UsMn.ts b/modules/cmpapi/src/encoder/section/UsMn.ts new file mode 100644 index 0000000..9848d65 --- /dev/null +++ b/modules/cmpapi/src/encoder/section/UsMn.ts @@ -0,0 +1,77 @@ +import { UsMnField } from "../field/UsMnField.js"; +import { EncodableSegment } from "../segment/EncodableSegment.js"; +import { UsMnCoreSegment } from "../segment/UsMnCoreSegment.js"; +import { UsMnGpcSegment } from "../segment/UsMnGpcSegment.js"; +import { AbstractLazilyEncodableSection } from "./AbstractLazilyEncodableSection.js"; + +export class UsMn extends AbstractLazilyEncodableSection { + public static readonly ID = 23; + public static readonly VERSION = 1; + public static readonly NAME = "usmn"; + + constructor(encodedString?: string) { + super(); + if (encodedString && encodedString.length > 0) { + this.decode(encodedString); + } + } + + //Overriden + public getId(): number { + return UsMn.ID; + } + + //Overriden + public getName(): string { + return UsMn.NAME; + } + + //Override + public getVersion(): number { + return UsMn.VERSION; + } + + //Overriden + protected initializeSegments(): EncodableSegment[] { + let segments: EncodableSegment[] = []; + segments.push(new UsMnCoreSegment()); + segments.push(new UsMnGpcSegment()); + return segments; + } + + //Overriden + protected decodeSection(encodedString: string): EncodableSegment[] { + let segments: EncodableSegment[] = this.initializeSegments(); + + if (encodedString != null && encodedString.length !== 0) { + let encodedSegments = encodedString.split("."); + + if (encodedSegments.length > 0) { + segments[0].decode(encodedSegments[0]); + } + + if (encodedSegments.length > 1) { + segments[1].setFieldValue(UsMnField.GPC_SEGMENT_INCLUDED, true); + segments[1].decode(encodedSegments[1]); + } else { + segments[1].setFieldValue(UsMnField.GPC_SEGMENT_INCLUDED, false); + } + } + + return segments; + } + + // Overriden + protected encodeSection(segments: EncodableSegment[]): string { + let encodedSegments: string[] = []; + + if (segments.length >= 1) { + encodedSegments.push(segments[0].encode()); + if (segments.length >= 2 && segments[1].getFieldValue(UsMnField.GPC_SEGMENT_INCLUDED) === true) { + encodedSegments.push(segments[1].encode()); + } + } + + return encodedSegments.join("."); + } +} diff --git a/modules/cmpapi/src/encoder/section/index.ts b/modules/cmpapi/src/encoder/section/index.ts index 3e54697..e84b3a5 100644 --- a/modules/cmpapi/src/encoder/section/index.ts +++ b/modules/cmpapi/src/encoder/section/index.ts @@ -10,6 +10,7 @@ export * from "./UsCt.js"; export * from "./UsDe.js"; export * from "./UsFl.js"; export * from "./UsIa.js"; +export * from "./UsMn.js"; export * from "./UsMt.js"; export * from "./UsNat.js"; export * from "./UsNe.js"; diff --git a/modules/cmpapi/src/encoder/segment/UsMnCoreSegment.ts b/modules/cmpapi/src/encoder/segment/UsMnCoreSegment.ts new file mode 100644 index 0000000..674f3f1 --- /dev/null +++ b/modules/cmpapi/src/encoder/segment/UsMnCoreSegment.ts @@ -0,0 +1,126 @@ +import { AbstractBase64UrlEncoder } from "../base64/AbstractBase64UrlEncoder.js"; +import { CompressedBase64UrlEncoder } from "../base64/CompressedBase64UrlEncoder.js"; +import { BitStringEncoder } from "../bitstring/BitStringEncoder.js"; +import { EncodableFixedInteger } from "../datatype/EncodableFixedInteger.js"; +import { EncodableFixedIntegerList } from "../datatype/EncodableFixedIntegerList.js"; +import { Predicate } from "../datatype/validate/Predicate.js"; +import { DecodingError } from "../error/DecodingError.js"; +import { ValidationError } from "../error/ValidationError.js"; +import { EncodableBitStringFields } from "../field/EncodableBitStringFields.js"; +import { UsMn_CORE_SEGMENT_FIELD_NAMES } from "../field/UsMnField.js"; +import { UsMnField } from "../field/UsMnField.js"; +import { UsMn } from "../section/UsMn.js"; +import { AbstractLazilyEncodableSegment } from "./AbstractLazilyEncodableSegment.js"; + +export class UsMnCoreSegment extends AbstractLazilyEncodableSegment { + private base64UrlEncoder: AbstractBase64UrlEncoder = CompressedBase64UrlEncoder.getInstance(); + private bitStringEncoder: BitStringEncoder = BitStringEncoder.getInstance(); + + constructor(encodedString?: string) { + super(); + if (encodedString) { + this.decode(encodedString); + } + } + + // overriden + public getFieldNames(): string[] { + return UsMn_CORE_SEGMENT_FIELD_NAMES; + } + + // overriden + protected initializeFields(): EncodableBitStringFields { + const nullableBooleanAsTwoBitIntegerValidator = new (class implements Predicate { + test(n: number): boolean { + return n >= 0 && n <= 2; + } + })(); + + const nonNullableBooleanAsTwoBitIntegerValidator = new (class implements Predicate { + test(n: number): boolean { + return n >= 1 && n <= 2; + } + })(); + const nullableBooleanAsTwoBitIntegerListValidator = new (class implements Predicate { + test(l: number[]): boolean { + for (let i = 0; i < l.length; i++) { + let n = l[i]; + if (n < 0 || n > 2) { + return false; + } + } + return true; + } + })(); + + let fields: EncodableBitStringFields = new EncodableBitStringFields(); + fields.put(UsMnField.VERSION.toString(), new EncodableFixedInteger(6, UsMn.VERSION)); + fields.put( + UsMnField.PROCESSING_NOTICE.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.SALE_OPT_OUT_NOTICE.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.SALE_OPT_OUT.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.TARGETED_ADVERTISING_OPT_OUT.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.SENSITIVE_DATA_PROCESSING.toString(), + new EncodableFixedIntegerList(2, [0, 0, 0, 0, 0, 0, 0, 0]).withValidator( + nullableBooleanAsTwoBitIntegerListValidator + ) + ); + fields.put( + UsMnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.ADDITIONAL_DATA_PROCESSING_CONSENT.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.MSPA_COVERED_TRANSACTION.toString(), + new EncodableFixedInteger(2, 1).withValidator(nonNullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.MSPA_OPT_OUT_OPTION_MODE.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + fields.put( + UsMnField.MSPA_SERVICE_PROVIDER_MODE.toString(), + new EncodableFixedInteger(2, 0).withValidator(nullableBooleanAsTwoBitIntegerValidator) + ); + return fields; + } + + // overriden + protected encodeSegment(fields: EncodableBitStringFields): string { + let bitString: string = this.bitStringEncoder.encode(fields, this.getFieldNames()); + let encodedString: string = this.base64UrlEncoder.encode(bitString); + return encodedString; + } + + // overriden + protected decodeSegment(encodedString: string, fields: EncodableBitStringFields): void { + if (encodedString == null || encodedString.length === 0) { + this.fields.reset(fields); + } + try { + let bitString: string = this.base64UrlEncoder.decode(encodedString); + this.bitStringEncoder.decode(bitString, this.getFieldNames(), fields); + } catch (e) { + throw new DecodingError("Unable to decode UsMnCoreSegment '" + encodedString + "'"); + } + } +} diff --git a/modules/cmpapi/src/encoder/segment/UsMnGpcSegment.ts b/modules/cmpapi/src/encoder/segment/UsMnGpcSegment.ts new file mode 100644 index 0000000..971ddc6 --- /dev/null +++ b/modules/cmpapi/src/encoder/segment/UsMnGpcSegment.ts @@ -0,0 +1,56 @@ +import { AbstractBase64UrlEncoder } from "../base64/AbstractBase64UrlEncoder.js"; +import { CompressedBase64UrlEncoder } from "../base64/CompressedBase64UrlEncoder.js"; +import { BitStringEncoder } from "../bitstring/BitStringEncoder.js"; +import { EncodableBoolean } from "../datatype/EncodableBoolean.js"; +import { EncodableFixedInteger } from "../datatype/EncodableFixedInteger.js"; +import { DecodingError } from "../error/DecodingError.js"; +import { EncodableBitStringFields } from "../field/EncodableBitStringFields.js"; +import { UsMn_GPC_SEGMENT_FIELD_NAMES } from "../field/UsMnField.js"; +import { UsMnField } from "../field/UsMnField.js"; +import { AbstractLazilyEncodableSegment } from "./AbstractLazilyEncodableSegment.js"; + +export class UsMnGpcSegment extends AbstractLazilyEncodableSegment { + private base64UrlEncoder: AbstractBase64UrlEncoder = CompressedBase64UrlEncoder.getInstance(); + private bitStringEncoder: BitStringEncoder = BitStringEncoder.getInstance(); + + constructor(encodedString?: string) { + super(); + if (encodedString) { + this.decode(encodedString); + } + } + + // overriden + public getFieldNames(): string[] { + return UsMn_GPC_SEGMENT_FIELD_NAMES; + } + + // overriden + protected initializeFields(): EncodableBitStringFields { + let fields: EncodableBitStringFields = new EncodableBitStringFields(); + fields.put(UsMnField.GPC_SEGMENT_TYPE.toString(), new EncodableFixedInteger(2, 1)); + fields.put(UsMnField.GPC_SEGMENT_INCLUDED.toString(), new EncodableBoolean(true)); + fields.put(UsMnField.GPC.toString(), new EncodableBoolean(false)); + return fields; + } + + // overriden + protected encodeSegment(fields: EncodableBitStringFields): string { + let bitString: string = this.bitStringEncoder.encode(fields, this.getFieldNames()); + let encodedString: string = this.base64UrlEncoder.encode(bitString); + return encodedString; + } + + // overriden + protected decodeSegment(encodedString: string, fields: EncodableBitStringFields): void { + if (encodedString == null || encodedString.length === 0) { + this.fields.reset(fields); + } + try { + let bitString: string = this.base64UrlEncoder.decode(encodedString); + this.bitStringEncoder.decode(bitString, this.getFieldNames(), fields); + } catch (e) { + throw new DecodingError("Unable to decode UsMnGpcSegment '" + encodedString + "'"); + } + } +} diff --git a/modules/cmpapi/src/encoder/segment/index.ts b/modules/cmpapi/src/encoder/segment/index.ts index 3bc0d7a..85cf8fb 100644 --- a/modules/cmpapi/src/encoder/segment/index.ts +++ b/modules/cmpapi/src/encoder/segment/index.ts @@ -26,6 +26,8 @@ export * from "./UsNhCoreSegment.js"; export * from "./UsNhGpcSegment.js"; export * from "./UsNjCoreSegment.js"; export * from "./UsNjGpcSegment.js"; +export * from "./UsMnCoreSegment.js"; +export * from "./UsMnGpcSegment.js"; export * from "./UsMtCoreSegment.js"; export * from "./UsMtGpcSegment.js"; export * from "./UsNatGpcSegment.js"; diff --git a/modules/cmpapi/test/GppModel.test.ts b/modules/cmpapi/test/GppModel.test.ts index c2406df..b036f26 100644 --- a/modules/cmpapi/test/GppModel.test.ts +++ b/modules/cmpapi/test/GppModel.test.ts @@ -59,6 +59,7 @@ describe("manifest.GppModel", (): void => { expect(gppModel.hasSection("usnh")).to.eql(false); expect(gppModel.hasSection("usnj")).to.eql(false); expect(gppModel.hasSection("ustn")).to.eql(false); + expect(gppModel.hasSection("usmn")).to.eql(false); gppModel.setFieldValue("tcfeuv2", "Version", 2); gppModel.setFieldValue("tcfeuv2", "Created", utcDateTime); @@ -83,6 +84,7 @@ describe("manifest.GppModel", (): void => { gppModel.setFieldValue("usnh", "Version", 1); gppModel.setFieldValue("usnj", "Version", 1); gppModel.setFieldValue("ustn", "Version", 1); + gppModel.setFieldValue("usmn", "Version", 1); expect(gppModel.hasSection("tcfeuv2")).to.eql(true); expect(gppModel.hasSection("tcfcav1")).to.eql(true); @@ -103,10 +105,11 @@ describe("manifest.GppModel", (): void => { expect(gppModel.hasSection("usnh")).to.eql(true); expect(gppModel.hasSection("usnj")).to.eql(true); expect(gppModel.hasSection("ustn")).to.eql(true); + expect(gppModel.hasSection("usmn")).to.eql(true); 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" + "DBACOYs~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~BAAAAAQA.QA" ); }); @@ -402,7 +405,7 @@ describe("manifest.GppModel", (): void => { it("should decode defaults from all sections", (): void => { let gppString = - "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"; + "DBACOYs~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~BAAAAAQA.QA"; let gppModel = new GppModel(gppString); expect(gppModel.hasSection("tcfeuv2")).to.eql(true); @@ -424,6 +427,7 @@ describe("manifest.GppModel", (): void => { expect(gppModel.hasSection("usnh")).to.eql(true); expect(gppModel.hasSection("usnj")).to.eql(true); expect(gppModel.hasSection("ustn")).to.eql(true); + expect(gppModel.hasSection("usmn")).to.eql(true); }); it("should decode uspv1 section", (): void => { diff --git a/modules/cmpapi/test/encoder/section/UsMn.test.ts b/modules/cmpapi/test/encoder/section/UsMn.test.ts new file mode 100644 index 0000000..07bbaed --- /dev/null +++ b/modules/cmpapi/test/encoder/section/UsMn.test.ts @@ -0,0 +1,124 @@ +import { expect } from "chai"; +import { UsMnField } from "../../../src/encoder/field/UsMnField"; +import { UsMn } from "../../../src/encoder/section/UsMn"; + +describe("manifest.section.UsMn", (): void => { + it("should encode default to BAAAAAQA.QA", (): void => { + let usMn = new UsMn(); + expect(usMn.encode()).to.eql("BAAAAAQA.QA"); + }); + + it("should encode to BVWSSVWA.YA", (): void => { + let usMn = new UsMn(); + + usMn.setFieldValue(UsMnField.PROCESSING_NOTICE, 1); + usMn.setFieldValue(UsMnField.SALE_OPT_OUT_NOTICE, 1); + usMn.setFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE, 1); + usMn.setFieldValue(UsMnField.SALE_OPT_OUT, 1); + usMn.setFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT, 1); + usMn.setFieldValue(UsMnField.SENSITIVE_DATA_PROCESSING, [2, 1, 0, 2, 1, 0, 2, 1]); + usMn.setFieldValue(UsMnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS, 1); + usMn.setFieldValue(UsMnField.ADDITIONAL_DATA_PROCESSING_CONSENT, 1); + usMn.setFieldValue(UsMnField.MSPA_COVERED_TRANSACTION, 1); + usMn.setFieldValue(UsMnField.MSPA_OPT_OUT_OPTION_MODE, 1); + usMn.setFieldValue(UsMnField.MSPA_SERVICE_PROVIDER_MODE, 2); + usMn.setFieldValue(UsMnField.GPC, true); + + expect(usMn.encode()).to.eql("BVWSSVWA.YA"); + }); + + it("should encode default to BAAAAAQA", (): void => { + let usMn = new UsMn(); + usMn.setFieldValue(UsMnField.GPC_SEGMENT_INCLUDED, false); + expect(usMn.encode()).to.eql("BAAAAAQA"); + }); + + it("should throw an error if invalid values are set", (): void => { + let usMn = new UsMn(); + + expect(function () { + usMn.setFieldValue(UsMnField.PROCESSING_NOTICE, 3); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.SALE_OPT_OUT_NOTICE, 3); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE, 3); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.SALE_OPT_OUT, 3); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT, -1); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.SENSITIVE_DATA_PROCESSING, [0, 1, 2, 3, 1, 2, 0, 1]); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS, [1, 2, 3]); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.ADDITIONAL_DATA_PROCESSING_CONSENT, 3); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.MSPA_COVERED_TRANSACTION, 0); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.MSPA_OPT_OUT_OPTION_MODE, 4); + }).to.throw(); + + expect(function () { + usMn.setFieldValue(UsMnField.MSPA_SERVICE_PROVIDER_MODE, -1); + }).to.throw(); + }); + + it("should decode BVWSSVWA.YA", (): void => { + let usMn = new UsMn("BVWSSVWA.YA"); + + expect(1, usMn.getFieldValue(UsMnField.PROCESSING_NOTICE)); + expect(1, usMn.getFieldValue(UsMnField.SALE_OPT_OUT_NOTICE)); + expect(1, usMn.getFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE)); + expect(1, usMn.getFieldValue(UsMnField.SALE_OPT_OUT)); + expect(1, usMn.getFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT)); + expect([2, 1, 0, 2, 1, 0, 2, 1], usMn.getFieldValue(UsMnField.SENSITIVE_DATA_PROCESSING)); + expect(1, usMn.getFieldValue(UsMnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS)); + expect(1, usMn.getFieldValue(UsMnField.ADDITIONAL_DATA_PROCESSING_CONSENT)); + expect(1, usMn.getFieldValue(UsMnField.MSPA_COVERED_TRANSACTION)); + expect(1, usMn.getFieldValue(UsMnField.MSPA_OPT_OUT_OPTION_MODE)); + expect(2, usMn.getFieldValue(UsMnField.MSPA_SERVICE_PROVIDER_MODE)); + expect(true, usMn.getFieldValue(UsMnField.GPC)); + expect(true, usMn.getFieldValue(UsMnField.GPC_SEGMENT_INCLUDED)); + }); + + it("should decode BVWSSVWA", (): void => { + let usMn = new UsMn("BVWSSVWA"); + + expect(1, usMn.getFieldValue(UsMnField.PROCESSING_NOTICE)); + expect(1, usMn.getFieldValue(UsMnField.SALE_OPT_OUT_NOTICE)); + expect(1, usMn.getFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE)); + expect(1, usMn.getFieldValue(UsMnField.SALE_OPT_OUT)); + expect(1, usMn.getFieldValue(UsMnField.TARGETED_ADVERTISING_OPT_OUT)); + expect([2, 1, 0, 2, 1, 0, 2, 1], usMn.getFieldValue(UsMnField.SENSITIVE_DATA_PROCESSING)); + expect(1, usMn.getFieldValue(UsMnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS)); + expect(1, usMn.getFieldValue(UsMnField.ADDITIONAL_DATA_PROCESSING_CONSENT)); + expect(1, usMn.getFieldValue(UsMnField.MSPA_COVERED_TRANSACTION)); + expect(1, usMn.getFieldValue(UsMnField.MSPA_OPT_OUT_OPTION_MODE)); + expect(2, usMn.getFieldValue(UsMnField.MSPA_SERVICE_PROVIDER_MODE)); + expect(false, usMn.getFieldValue(UsMnField.GPC_SEGMENT_INCLUDED)); + }); + + it("should throw Error on garbage", (): void => { + expect(function () { + new UsMn("z").getFieldValue(UsMnField.SALE_OPT_OUT); + }).to.throw("Unable to decode UsMnCoreSegment 'z'"); + }); +});