From 6f80bc95a28886fc69d4b9036c227494225dff3a Mon Sep 17 00:00:00 2001 From: George Bardis <109069547+bardisg@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:22:33 +0200 Subject: [PATCH] feat: add generic consent management fields in initially onboarded destinations config (#1058) --- package.json | 3 +- scripts/run-schema-validation.sh | 0 scripts/schemaGenerator.py | 182 +++++-- .../destinations/customerio/db-config.json | 18 +- .../destinations/customerio/schema.json | 465 ++++++++++++++++++ .../destinations/customerio/ui-config.json | 211 ++++++-- .../destinations/ga4/db-config.json | 22 +- .../destinations/ga4/schema.json | 443 +++++++++++++++++ .../destinations/ga4/ui-config.json | 216 ++++++-- .../destinations/mp/db-config.json | 11 +- .../destinations/mp/schema.json | 419 ++++++++++++++++ .../destinations/mp/ui-config.json | 107 +++- .../destinations/snowflake/db-config.json | 12 +- .../destinations/snowflake/schema.json | 419 ++++++++++++++++ .../destinations/snowflake/ui-config.json | 107 +++- test/configData/ui-config.json | 12 + .../validation/destinations/customerio.json | 103 ++++ test/data/validation/destinations/ga4.json | 135 +++++ test/data/validation/destinations/mp.json | 98 ++++ .../validation/destinations/snowflake.json | 118 +++++ test/validation.test.ts | 8 +- 21 files changed, 2958 insertions(+), 151 deletions(-) mode change 100644 => 100755 scripts/run-schema-validation.sh diff --git a/package.json b/package.json index ad3500bb5..63938c26a 100755 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "update:schema:source": "python3 scripts/schemaGenerator.py source -update -name ", "check:schema:destination:all": "python3 scripts/schemaGenerator.py destination -all", "check:schema:destination": "python3 scripts/schemaGenerator.py destination -name ", - "update:schema:destination": "python3 scripts/schemaGenerator.py destination -update -name " + "update:schema:destination": "python3 scripts/schemaGenerator.py destination -update -name ", + "deploy:db:local": "python3 scripts/deployToDB.py http://localhost:5050" }, "devDependencies": { "@babel/core": "^7.21.3", diff --git a/scripts/run-schema-validation.sh b/scripts/run-schema-validation.sh old mode 100644 new mode 100755 diff --git a/scripts/schemaGenerator.py b/scripts/schemaGenerator.py index 0f99cf66e..96e590670 100644 --- a/scripts/schemaGenerator.py +++ b/scripts/schemaGenerator.py @@ -73,6 +73,8 @@ def generalize_regex_pattern(field): if defaultEnvPattern not in pattern and (('value' not in field or field['value'] != 'purpose') and ('configKey' not in field or field['configKey'] != 'purpose')): indexToPlace = pattern.find(defaultSubPattern) + len(defaultSubPattern) pattern = pattern[:indexToPlace] + '|' + defaultEnvPattern + pattern[indexToPlace:] + # TODO: we should not use a case here for the individual properties. Just pass the desired pattern as regex property + # in ketch purpose fields and delete next case elif ('value' in field and field['value'] == 'purpose') or ('configKey' in field and field['configKey'] == 'purpose'): pattern = '^(.{0,100})$' else: @@ -121,7 +123,7 @@ def is_field_present_in_default_config(field, dbConfig, schema_field_name): return False def generate_schema_for_default_checkbox(field, dbConfig, schema_field_name): - """Creates an schema object of defaultCheckbox. + """Creates a schema object of defaultCheckbox. Args: field (object): Individual field in ui-config. @@ -144,7 +146,7 @@ def generate_schema_for_default_checkbox(field, dbConfig, schema_field_name): def generate_schema_for_checkbox(field, dbConfig, schema_field_name): - """Creates an schema object of checkbox. + """Creates a schema object of checkbox. Args: field (object): Individual field in ui-config. @@ -173,7 +175,7 @@ def generate_schema_for_checkbox(field, dbConfig, schema_field_name): def generate_schema_for_textinput(field, dbConfig, schema_field_name): - """Creates an schema object of textinput. + """Creates a schema object of textinput. Args: field (object): Individual field in ui-config. @@ -204,7 +206,7 @@ def generate_schema_for_textinput(field, dbConfig, schema_field_name): def generate_schema_for_textarea_input(field, dbConfig, schema_field_name): - """Creates an schema object of textareaInput. + """Creates a schema object of textareaInput. Args: field (object): Individual field in ui-config. @@ -222,7 +224,7 @@ def generate_schema_for_textarea_input(field, dbConfig, schema_field_name): def generate_schema_for_single_select(field, dbConfig, schema_field_name): - """Creates an schema object of singleSelect. + """Creates a schema object of singleSelect. Args: field (object): Individual field in ui-config. @@ -269,7 +271,7 @@ def generate_schema_for_single_select(field, dbConfig, schema_field_name): def generate_schema_for_dynamic_custom_form(field, dbConfig, schema_field_name): - """Creates an schema object of dynamicCustomForm. + """Creates a schema object of dynamicCustomForm. Args: field (object): Individual field in ui-config. @@ -285,21 +287,39 @@ def generate_schema_for_dynamic_custom_form(field, dbConfig, schema_field_name): dynamicCustomFormItemObj = {} dynamicCustomFormItemObj["type"] = FieldTypeEnum.OBJECT.value dynamicCustomFormItemObj["properties"] = {} - for customField in field["customFields"]: - customeFieldSchemaObj = uiTypetoSchemaFn.get(customField["type"])(customField, dbConfig, schema_field_name) + allOfSchemaObj = {} + + # For old schema types customFields contains the children, for v2 its is rowFields + customFieldsKey = "customFields" + if "rowFields" in field: + customFieldsKey = "rowFields" + + allOfSchemaObj = generate_schema_for_dynamic_custom_form_allOf(field[customFieldsKey], dbConfig, schema_field_name) + + for customField in field[customFieldsKey]: + customFieldSchemaObj = uiTypetoSchemaFn.get(customField["type"])(customField, dbConfig, schema_field_name) isCustomFieldDependentOnSource = is_dest_field_dependent_on_source(customField, dbConfig, schema_field_name) - if 'pattern' not in customeFieldSchemaObj and not isCustomFieldDependentOnSource and customeFieldSchemaObj["type"]==FieldTypeEnum.STRING.value: - customeFieldSchemaObj["pattern"] = generalize_regex_pattern(customField) + + if "preRequisites" in customField: + continue + + if 'pattern' not in customFieldSchemaObj and not isCustomFieldDependentOnSource and customFieldSchemaObj["type"] == FieldTypeEnum.STRING.value and customField["type"] != "singleSelect" and customField["type"] != "dynamicSelectForm": + customFieldSchemaObj["pattern"] = generalize_regex_pattern(customField) + # If the custom field is source dependent, we remove the source keys as it's not required inside custom fields, rather they need to be moved to top. if isCustomFieldDependentOnSource: for sourceType in dbConfig["supportedSourceTypes"]: if sourceType in dbConfig["destConfig"] and field[schema_field_name] in dbConfig["destConfig"][sourceType]: - customeFieldSchemaObj = customeFieldSchemaObj["properties"][sourceType] + customFieldSchemaObj = customFieldSchemaObj["properties"][sourceType] break - dynamicCustomFormItemObj["properties"][customField[schema_field_name]] = customeFieldSchemaObj + dynamicCustomFormItemObj["properties"][customField[schema_field_name]] = customFieldSchemaObj + + if allOfSchemaObj: + dynamicCustomFormItemObj['allOf'] = allOfSchemaObj dynamicCustomFormObj["items"] = dynamicCustomFormItemObj isSourceDependent = is_dest_field_dependent_on_source(field, dbConfig, schema_field_name) + # If the field is source dependent, new schema object is created by setting the fields inside the source. if isSourceDependent: newDynamicCustomFormObj = {"type": FieldTypeEnum.OBJECT.value} @@ -308,11 +328,62 @@ def generate_schema_for_dynamic_custom_form(field, dbConfig, schema_field_name): if sourceType in dbConfig["destConfig"] and field[schema_field_name] in dbConfig["destConfig"][sourceType]: newDynamicCustomFormObj["properties"][sourceType] = dynamicCustomFormObj dynamicCustomFormObj = newDynamicCustomFormObj + return dynamicCustomFormObj + +def generate_schema_for_dynamic_custom_form_allOf(customFields, dbConfig, schema_field_name): + """Creates the allOf structure of schema, empty if not required. + - Finds the list of unique preRequisites. + - For each unique preRequisites, the properties are found by matching the current preRequisites. + - preRequisites becomes if block and corresponding properties become then block. + + Args: + customFields (collection): child fields from file content of ui-config.json. + dbConfig (object): Configurations of db-config.json. + schema_field_name (string): Specifies which key has the field's name in schema. + For old schema types, it is 'value' else 'configKey'. + + Returns: + object: allOf object of schema + """ + allOfItemList = [] + preRequisitesList = [] + + for field in customFields: + if "preRequisites" not in field: + continue + isPresent = False + for preRequisites in preRequisitesList: + if compare_pre_requisite_fields(preRequisites, field["preRequisites"]["fields"], True): + isPresent = True + break + if not isPresent: + preRequisitesList.append(field["preRequisites"]["fields"]) + + for preRequisites in preRequisitesList: + ifObj = generate_if_object(preRequisites, True) + thenObj = {"properties": {}, "required": []} + allOfItemObj = {"if": ifObj} + + for field in customFields: + if "preRequisites" not in field: + continue + if compare_pre_requisite_fields(field["preRequisites"]["fields"], preRequisites, True): + thenObj["properties"][field[schema_field_name]] = uiTypetoSchemaFn.get(field["type"])(field, dbConfig, schema_field_name) + if "required" in field and field["required"] == True: + thenObj["required"].append(field[schema_field_name]) + allOfItemObj["then"] = thenObj + allOfItemList.append(allOfItemObj) + + # Calling anyOf to check if two conditions can be grouped as anyOf. + allOfItemList = generate_schema_for_anyOf(allOfItemList, schema_field_name) + return allOfItemList + + def generate_schema_for_dynamic_form(field, dbConfig, schema_field_name): - """Creates an schema object of dynamicForm. + """Creates a schema object of dynamicForm. Args: field (object): Individual field in ui-config. @@ -363,7 +434,7 @@ def generate_key(forFieldWithTo): def generate_schema_for_dynamic_select_form(field, dbConfig, schema_field_name): - """Creates an schema object of dynamicSelectForm. + """Creates a schema object of dynamicSelectForm. Args: field (object): Individual field in ui-config. @@ -377,7 +448,7 @@ def generate_schema_for_dynamic_select_form(field, dbConfig, schema_field_name): return generate_schema_for_dynamic_form(field, dbConfig, schema_field_name) def generate_schema_for_mapping(field, dbConfig, schema_field_name): - """Creates an schema object of mapping. + """Creates a schema object of mapping. Args: field (object): Individual field in ui-config. @@ -392,7 +463,7 @@ def generate_schema_for_mapping(field, dbConfig, schema_field_name): def generate_schema_for_tag_input(field, dbConfig, schema_field_name): - """Creates an schema object of tagInput. + """Creates a schema object of tagInput. Args: field (object): Individual field in ui-config. @@ -428,7 +499,7 @@ def generate_schema_for_tag_input(field, dbConfig, schema_field_name): def generate_schema_for_time_range_picker(field, dbConfig, schema_field_name): - """Creates an schema object of timeRangePicker. + """Creates a schema object of timeRangePicker. Args: field (object): Individual field in ui-config. @@ -451,7 +522,7 @@ def generate_schema_for_time_range_picker(field, dbConfig, schema_field_name): def generate_schema_for_time_picker(field, dbConfig, schema_field_name): - """Creates an schema object of timePicker. + """Creates a schema object of timePicker. Args: field (object): Individual field in ui-config. @@ -466,26 +537,35 @@ def generate_schema_for_time_picker(field, dbConfig, schema_field_name): "type": FieldTypeEnum.STRING.value } -def compare_pre_requisite_fields(fieldA, fieldB): - """Compares two preRequisiteFields fieldA and fieldB for each property and checks if there "selectedValue" match. + +def compare_pre_requisite_fields(fieldA, fieldB, isV2 = False): + """Compares two preRequisiteFields fieldA and fieldB for each property and checks if their value matches. Args: - fieldA (list or object): contains two properties, 'name' and 'selectedValue'. - fieldB (list or object): + fieldA (list or object): contains two properties representing 'name' and 'selectedValue'. + fieldB (list or object): contains two properties representing 'name' and 'selectedValue'. + isV2 (bool): determines if new property names should be used Returns: boolean: If all the properties have the same 'name' and 'selectedValue', then it returns True else False. - """ + """ + valueKey = 'selectedValue' + nameKey = 'name' + + if isV2: + valueKey = 'value' + nameKey = 'configKey' + if type(fieldA) != type(fieldB): return False elif type(fieldA) == list: if len(fieldA) != len(fieldB): return False for i in range(0, len(fieldA)): - if fieldA[i]['name'] != fieldB[i]['name'] or fieldA[i]['selectedValue'] != fieldB[i]['selectedValue']: + if fieldA[i][nameKey] != fieldB[i][nameKey] or fieldA[i][valueKey] != fieldB[i][valueKey]: return False else: - if fieldA['name'] != fieldB['name'] or fieldA['selectedValue'] != fieldB['selectedValue']: + if fieldA[nameKey] != fieldB[nameKey] or fieldA[valueKey] != fieldB[valueKey]: return False return True @@ -514,27 +594,35 @@ def get_unique_pre_requisite_fields(uiConfig): return preRequisiteFieldsList -def generate_if_object(preRequisiteField): +def generate_if_object(preRequisiteField, isV2 = False): """Creates an if object for the given preRequisiteField. The preRequisiteField becomes an if condition in the schema. Args: preRequisiteField (list or object): contains two properties, 'name' and 'selectedValue'. + isV2 (bool): if it should use the v2 or the legacy property key names Returns: object: if block for given preRequisiteField. - """ + """ ifObj = {"properties": {}, "required": []} + valueKey = 'selectedValue' + nameKey = 'name' + + if isV2: + valueKey = 'value' + nameKey = 'configKey' + if type(preRequisiteField) == list: for field in preRequisiteField: - ifObj["properties"][field["name"]] = { - "const": field["selectedValue"] + ifObj["properties"][field[nameKey]] = { + "const": field[valueKey] } - ifObj["required"].append(field["name"]) + ifObj["required"].append(field[nameKey]) else: - ifObj["properties"][preRequisiteField["name"]] = { - "const": preRequisiteField["selectedValue"] + ifObj["properties"][preRequisiteField[nameKey]] = { + "const": preRequisiteField[valueKey] } - ifObj["required"].append(preRequisiteField["name"]) + ifObj["required"].append(preRequisiteField[nameKey]) return ifObj @@ -764,6 +852,7 @@ def generate_schema_properties(uiConfig, dbConfig, schemaObject, properties, nam if selector == 'destination': baseTemplate = uiConfig.get('baseTemplate', []) sdkTemplate = uiConfig.get('sdkTemplate', {}) + consentSettingsTemplate = uiConfig.get('consentSettingsTemplate', {}) for template in baseTemplate: for section in template.get('sections', []): for group in section.get('groups', []): @@ -785,6 +874,14 @@ def generate_schema_properties(uiConfig, dbConfig, schemaObject, properties, nam if field.get('required', False) == True and is_field_present_in_default_config(field, dbConfig, "configKey"): schemaObject['required'].append(field['configKey']) + for field in consentSettingsTemplate.get('fields', []): + generateFunction = uiTypetoSchemaFn.get(field['type'], None) + if generateFunction: + properties[field['configKey']] = generateFunction( + field, dbConfig, 'configKey') + if field.get('required', False) == True and is_field_present_in_default_config(field, dbConfig, "configKey"): + schemaObject['required'].append(field['configKey']) + # default properties in new ui-config based schemas. schemaObject['properties']['useNativeSDK'] = generate_schema_for_checkbox({"type":"checkbox", "value":"useNativeSDK"}, dbConfig, "value") @@ -890,6 +987,7 @@ def generate_warnings_for_each_type(uiConfig, dbConfig, schema, curUiType): else: baseTemplate = uiConfig.get('baseTemplate', []) sdkTemplate = uiConfig.get('sdkTemplate', {}) + consentSettingsTemplate = uiConfig.get('consentSettingsTemplate', {}) for template in baseTemplate: for section in template.get('sections', []): for group in section.get('groups', []): @@ -931,6 +1029,24 @@ def generate_warnings_for_each_type(uiConfig, dbConfig, schema, curUiType): warnings.warn("For type:{} field:{} Difference is : \n\n {} \n".format( curUiType, field["configKey"], schemaDiff), UserWarning) + for field in consentSettingsTemplate.get('fields', []): + if "preRequisites" in field: + continue + generateFunction = uiTypetoSchemaFn.get(field['type'], None) + if generateFunction: + if generateFunction and field["type"] == curUiType: + if field["configKey"] not in schema["properties"]: + warnings.warn( + f'{field["configKey"]} field is not in schema \n', UserWarning) + else: + curSchemaField = schema["properties"][field["configKey"]] + newSchemaField = uiTypetoSchemaFn.get( + curUiType)(field, dbConfig, "configKey") + schemaDiff = diff(newSchemaField, curSchemaField) + if schemaDiff: + warnings.warn("For type:{} field:{} Difference is : \n\n {} \n".format( + curUiType, field["configKey"], schemaDiff), UserWarning) + uiTypetoSchemaFn = { "defaultCheckbox": generate_schema_for_default_checkbox, diff --git a/src/configurations/destinations/customerio/db-config.json b/src/configurations/destinations/customerio/db-config.json index 310a6d308..8411d5188 100644 --- a/src/configurations/destinations/customerio/db-config.json +++ b/src/configurations/destinations/customerio/db-config.json @@ -14,6 +14,7 @@ "whitelistedEvents", "oneTrustCookieCategories", "ketchConsentPurposes", + "consentManagement", "eventFilteringOption", "sendPageNameInSDK", "dataUseInApp" @@ -53,7 +54,22 @@ "oneTrustCookieCategories", "ketchConsentPurposes" ], - "web": ["connectionMode", "dataUseInApp", "useNativeSDK", "sendPageNameInSDK"] + "android": ["consentManagement"], + "ios": ["consentManagement"], + "web": [ + "connectionMode", + "dataUseInApp", + "useNativeSDK", + "sendPageNameInSDK", + "consentManagement" + ], + "unity": ["consentManagement"], + "amp": ["consentManagement"], + "warehouse": ["consentManagement"], + "reactnative": ["consentManagement"], + "flutter": ["consentManagement"], + "cordova": ["consentManagement"], + "shopify": ["consentManagement"] }, "secretKeys": [] } diff --git a/src/configurations/destinations/customerio/schema.json b/src/configurations/destinations/customerio/schema.json index 8c74ee62c..d34677d77 100644 --- a/src/configurations/destinations/customerio/schema.json +++ b/src/configurations/destinations/customerio/schema.json @@ -103,6 +103,471 @@ } } } + }, + "consentManagement": { + "type": "object", + "properties": { + "android": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "ios": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "web": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "unity": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "amp": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "warehouse": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "reactnative": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "flutter": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "cordova": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "shopify": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + } + } } } } diff --git a/src/configurations/destinations/customerio/ui-config.json b/src/configurations/destinations/customerio/ui-config.json index 6aab25384..d1b515603 100644 --- a/src/configurations/destinations/customerio/ui-config.json +++ b/src/configurations/destinations/customerio/ui-config.json @@ -79,7 +79,7 @@ "icon": "settings", "groups": [ { - "title": "Destiantion Specific Setting", + "title": "Destination Specific Setting", "note": "Configure a few Customerio settings here", "fields": [ { @@ -97,6 +97,90 @@ } ] }, + { + "id": "consentSettings", + "title": "Consent settings", + "note": "Configure consent settings for each provider here", + "icon": "settings", + "groups": [ + { + "title": "OneTrust consent settings", + "note": [ + "Enter your OneTrust consent category IDs if you have them configured. The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", + { + "text": "Learn more ", + "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/consent-manager/onetrust/" + }, + "about RudderStack's OneTrust Consent Management feature." + ], + "fields": [ + { + "type": "tagInput", + "label": "Consent categories", + "note": "Input your OneTrust category IDs by pressing 'Enter' after each entry.", + "configKey": "oneTrustCookieCategories", + "tagKey": "oneTrustCookieCategory", + "placeholder": "e.g: C0001", + "default": [ + { + "oneTrustCookieCategory": "" + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } + }, + { + "title": "Ketch consent purpose settings", + "note": [ + "Enter your Ketch purpose Id if you have them configured. ", + { + "text": "Learn more ", + "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/consent-manager/ketch/" + }, + "about RudderStack and Ketch Consent Manager integration." + ], + "fields": [ + { + "type": "tagInput", + "label": "Purpose ID", + "note": "Input your Ketch purpose Id by pressing 'Enter' after each entry", + "configKey": "ketchConsentPurposes", + "tagKey": "purpose", + "placeholder": "e.g: Marketing", + "default": [ + { + "purpose": "" + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } + } + ] + }, { "title": "Other settings", "note": "Configure advanced RudderStack features here", @@ -178,51 +262,6 @@ } } ] - }, - { - "title": "OneTrust cookie consent settings", - "note": [ - "Enter your OneTrust category names if you have them configured. ", - { - "text": "Learn more ", - "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/rudderstack-javascript-sdk/onetrust-consent-manager/" - }, - "about RudderStack’s OneTrust Consent Manager feature." - ], - "fields": [ - { - "type": "tagInput", - "label": "Cookie category name", - "note": "Input your OneTrust category names by pressing ‘Enter’ after each entry", - "configKey": "oneTrustCookieCategories", - "tagKey": "oneTrustCookieCategory", - "placeholder": "e.g: Credit card visit", - "default": [ - { - "oneTrustCookieCategory": "" - } - ] - } - ] - }, - { - "title": "Ketch consent settings", - "note": "Enter your Ketch Consent Purposes if you have them configured.", - "fields": [ - { - "type": "tagInput", - "label": "Ketch consent purpose", - "note": "Input your Ketch consent purpose by pressing ‘Enter’ after each entry", - "configKey": "ketchConsentPurposes", - "tagKey": "purpose", - "placeholder": "e.g: Marketing", - "default": [ - { - "purpose": "" - } - ] - } - ] } ] } @@ -248,6 +287,86 @@ "default": false } ] + }, + "consentSettingsTemplate": { + "title": "Consent settings", + "note": "not visible in the ui", + "fields": [ + { + "type": "dynamicCustomForm", + "configKey": "consentManagement", + "default": [], + "rowFields": [ + { + "type": "singleSelect", + "label": "Consent management provider", + "configKey": "provider", + "options": [ + { + "label": "Custom", + "value": "custom" + }, + { + "label": "Ketch", + "value": "ketch" + }, + { + "label": "OneTrust", + "value": "oneTrust" + } + ], + "default": "oneTrust", + "required": true + }, + { + "type": "singleSelect", + "label": "the required consent logic", + "configKey": "resolutionStrategy", + "options": [ + { + "label": "AND", + "value": "and" + }, + { + "label": "OR", + "value": "or" + } + ], + "required": true, + "variant": "badge", + "preRequisites": { + "fields": [ + { + "configKey": "provider", + "value": "custom" + } + ] + } + }, + { + "type": "tagInput", + "label": "Enter consent category ID’s", + "note": "Input your consent category IDs by pressing ‘Enter’ after each entry. The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", + "configKey": "consents", + "tagKey": "consent", + "placeholder": "e.g: Marketing", + "default": [ + { + "consent": "" + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": true + } + ] + } + } + ] } } } diff --git a/src/configurations/destinations/ga4/db-config.json b/src/configurations/destinations/ga4/db-config.json index c85dcd8cb..48278279d 100644 --- a/src/configurations/destinations/ga4/db-config.json +++ b/src/configurations/destinations/ga4/db-config.json @@ -15,9 +15,10 @@ "eventFilteringOption", "extendPageViewParams", "piiPropertiesToIgnore", - "oneTrustCookieCategories", "overrideClientAndSessionId", - "ketchConsentPurposes" + "oneTrustCookieCategories", + "ketchConsentPurposes", + "consentManagement" ], "excludeKeys": [], "supportedSourceTypes": [ @@ -68,8 +69,8 @@ "blacklistedEvents", "eventFilteringOption", "piiPropertiesToIgnore", - "ketchConsentPurposes", - "oneTrustCookieCategories" + "oneTrustCookieCategories", + "ketchConsentPurposes" ], "web": [ "debugView", @@ -78,10 +79,17 @@ "capturePageView", "useNativeSDKToSend", "extendPageViewParams", - "overrideClientAndSessionId" + "overrideClientAndSessionId", + "consentManagement" ], - "android": ["useNativeSDK", "connectionMode"], - "ios": ["useNativeSDK", "connectionMode"] + "android": ["useNativeSDK", "connectionMode", "consentManagement"], + "ios": ["useNativeSDK", "connectionMode", "consentManagement"], + "unity": ["consentManagement"], + "amp": ["consentManagement"], + "reactnative": ["consentManagement"], + "flutter": ["consentManagement"], + "cordova": ["consentManagement"], + "shopify": ["consentManagement"] }, "secretKeys": ["apiSecret"] }, diff --git a/src/configurations/destinations/ga4/schema.json b/src/configurations/destinations/ga4/schema.json index a1dce8048..ee55cb8ad 100644 --- a/src/configurations/destinations/ga4/schema.json +++ b/src/configurations/destinations/ga4/schema.json @@ -82,6 +82,425 @@ } } }, + "consentManagement": { + "type": "object", + "properties": { + "android": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "ios": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "web": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "unity": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "amp": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "reactnative": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "flutter": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "cordova": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "shopify": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + } + } + }, "capturePageView": { "type": "object", "properties": { @@ -144,6 +563,30 @@ "web": { "type": "string", "enum": ["cloud", "device", "hybrid"] + }, + "unity": { + "type": "string", + "enum": ["cloud"] + }, + "amp": { + "type": "string", + "enum": ["cloud"] + }, + "reactnative": { + "type": "string", + "enum": ["cloud"] + }, + "flutter": { + "type": "string", + "enum": ["cloud"] + }, + "cordova": { + "type": "string", + "enum": ["cloud"] + }, + "shopify": { + "type": "string", + "enum": ["cloud"] } } } diff --git a/src/configurations/destinations/ga4/ui-config.json b/src/configurations/destinations/ga4/ui-config.json index 1f0987c4c..b267e427e 100644 --- a/src/configurations/destinations/ga4/ui-config.json +++ b/src/configurations/destinations/ga4/ui-config.json @@ -146,6 +146,90 @@ } ] }, + { + "id": "consentSettings", + "title": "Consent settings", + "note": "Configure consent settings for each provider here", + "icon": "settings", + "groups": [ + { + "title": "OneTrust consent settings", + "note": [ + "Enter your OneTrust consent category IDs if you have them configured. The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", + { + "text": "Learn more ", + "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/consent-manager/onetrust/" + }, + "about RudderStack's OneTrust Consent Management feature." + ], + "fields": [ + { + "type": "tagInput", + "label": "Consent categories", + "note": "Input your OneTrust category IDs by pressing 'Enter' after each entry.", + "configKey": "oneTrustCookieCategories", + "tagKey": "oneTrustCookieCategory", + "placeholder": "e.g: C0001", + "default": [ + { + "oneTrustCookieCategory": "" + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } + }, + { + "title": "Ketch consent purpose settings", + "note": [ + "Enter your Ketch purpose Id if you have them configured. ", + { + "text": "Learn more ", + "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/consent-manager/ketch/" + }, + "about RudderStack and Ketch Consent Manager integration." + ], + "fields": [ + { + "type": "tagInput", + "label": "Purpose ID", + "note": "Input your Ketch purpose Id by pressing 'Enter' after each entry", + "configKey": "ketchConsentPurposes", + "tagKey": "purpose", + "placeholder": "e.g: Marketing", + "default": [ + { + "purpose": "" + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } + } + ] + }, { "title": "Other settings", "note": "Configure advanced RudderStack features here", @@ -232,58 +316,6 @@ } } ] - }, - { - "title": "OneTrust consent settings", - "note": [ - "Enter your OneTrust consent category IDs if you have them configured. The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", - { - "text": "Learn more ", - "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/consent-manager/onetrust/" - }, - "about RudderStack's OneTrust Consent Management feature." - ], - "fields": [ - { - "type": "tagInput", - "label": "Consent categories", - "note": "Input your OneTrust category IDs by pressing 'Enter' after each entry.", - "configKey": "oneTrustCookieCategories", - "tagKey": "oneTrustCookieCategory", - "placeholder": "e.g: C0001", - "default": [ - { - "oneTrustCookieCategory": "" - } - ] - } - ] - }, - { - "title": "Ketch consent purpose settings", - "note": [ - "Enter your Ketch purpose Id if you have them configured. ", - { - "text": "Learn more ", - "link": "https://www.rudderstack.com/docs/sources/event-streams/sdks/consent-manager/ketch/" - }, - "about RudderStack and Ketch Consent Manager integration." - ], - "fields": [ - { - "type": "tagInput", - "label": "Purpose ID", - "note": "Input your Ketch purpose Id by pressing 'Enter' after each entry", - "configKey": "ketchConsentPurposes", - "tagKey": "purpose", - "placeholder": "e.g: Marketing", - "default": [ - { - "purpose": "" - } - ] - } - ] } ] } @@ -361,6 +393,86 @@ "default": true } ] + }, + "consentSettingsTemplate": { + "title": "Consent settings", + "note": "not visible in the ui", + "fields": [ + { + "type": "dynamicCustomForm", + "configKey": "consentManagement", + "default": [], + "rowFields": [ + { + "type": "singleSelect", + "label": "Consent management provider", + "configKey": "provider", + "options": [ + { + "label": "Custom", + "value": "custom" + }, + { + "label": "Ketch", + "value": "ketch" + }, + { + "label": "OneTrust", + "value": "oneTrust" + } + ], + "default": "oneTrust", + "required": true + }, + { + "type": "singleSelect", + "label": "the required consent logic", + "configKey": "resolutionStrategy", + "options": [ + { + "label": "AND", + "value": "and" + }, + { + "label": "OR", + "value": "or" + } + ], + "required": true, + "variant": "badge", + "preRequisites": { + "fields": [ + { + "configKey": "provider", + "value": "custom" + } + ] + } + }, + { + "type": "tagInput", + "label": "Enter consent category ID’s", + "note": "Input your consent category IDs by pressing ‘Enter’ after each entry. The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", + "configKey": "consents", + "tagKey": "consent", + "placeholder": "e.g: Marketing", + "default": [ + { + "consent": "" + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": true + } + ] + } + } + ] } } } diff --git a/src/configurations/destinations/mp/db-config.json b/src/configurations/destinations/mp/db-config.json index 378743d71..e313f0a10 100644 --- a/src/configurations/destinations/mp/db-config.json +++ b/src/configurations/destinations/mp/db-config.json @@ -29,6 +29,7 @@ "whitelistedEvents", "oneTrustCookieCategories", "ketchConsentPurposes", + "consentManagement", "eventFilteringOption", "identityMergeApi", "ignoreDnt" @@ -101,7 +102,15 @@ "strictMode", "ignoreDnt" ], - "web": ["useNativeSDK"] + "android": ["consentManagement"], + "web": ["useNativeSDK", "consentManagement"], + "ios": ["consentManagement"], + "unity": ["consentManagement"], + "amp": ["consentManagement"], + "reactnative": ["consentManagement"], + "flutter": ["consentManagement"], + "cordova": ["consentManagement"], + "shopify": ["consentManagement"] }, "secretKeys": ["token", "gdprApiToken"] } diff --git a/src/configurations/destinations/mp/schema.json b/src/configurations/destinations/mp/schema.json index cccaa19ae..3a20b0006 100644 --- a/src/configurations/destinations/mp/schema.json +++ b/src/configurations/destinations/mp/schema.json @@ -221,6 +221,425 @@ } } } + }, + "consentManagement": { + "type": "object", + "properties": { + "android": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "ios": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "web": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "unity": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "amp": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "reactnative": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "flutter": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "cordova": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "shopify": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + } + } } }, "anyOf": [ diff --git a/src/configurations/destinations/mp/ui-config.json b/src/configurations/destinations/mp/ui-config.json index 29977215f..9a5e9c565 100644 --- a/src/configurations/destinations/mp/ui-config.json +++ b/src/configurations/destinations/mp/ui-config.json @@ -422,7 +422,19 @@ "label": "Category ID", "required": false } - ] + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } }, { "type": "dynamicCustomForm", @@ -437,7 +449,98 @@ "regex": "^(.{0,100})$", "required": false } - ] + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } + }, + { + "type": "dynamicCustomForm", + "value": "consentManagement", + "label": "Consent management settings", + "footerNote": "The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", + "customFields": [ + { + "type": "singleSelect", + "label": "Consent management provider", + "value": "provider", + "options": [ + { + "name": "Custom", + "value": "custom" + }, + { + "name": "Ketch", + "value": "ketch" + }, + { + "name": "OneTrust", + "value": "oneTrust" + } + ], + "defaultOption": { + "name": "OneTrust", + "value": "oneTrust" + }, + "required": true + }, + { + "type": "singleSelect", + "label": "the required consent logic", + "value": "resolutionStrategy", + "options": [ + { + "name": "AND", + "value": "and" + }, + { + "name": "OR", + "value": "or" + } + ], + "required": true, + "variant": "badge", + "preRequisites": { + "fields": [ + { + "configKey": "provider", + "value": "custom" + } + ] + } + }, + { + "type": "dynamicCustomForm", + "value": "consents", + "label": "Enter consent category ID’s", + "customFields": [ + { + "type": "textInput", + "placeholder": "Marketing", + "value": "consent", + "regex": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$", + "required": false + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": true + } + ] + } } ] } diff --git a/src/configurations/destinations/snowflake/db-config.json b/src/configurations/destinations/snowflake/db-config.json index dfe0b741f..03c4c97ed 100644 --- a/src/configurations/destinations/snowflake/db-config.json +++ b/src/configurations/destinations/snowflake/db-config.json @@ -5,7 +5,6 @@ "config": { "transformAtV1": "processor", "saveDestinationResponse": true, - "includeKeys": ["oneTrustCookieCategories", "ketchConsentPurposes"], "excludeKeys": [], "supportedSourceTypes": [ "android", @@ -62,7 +61,16 @@ "useRudderStorage", "oneTrustCookieCategories", "ketchConsentPurposes" - ] + ], + "android": ["consentManagement"], + "ios": ["consentManagement"], + "web": ["useNativeSDK", "consentManagement"], + "unity": ["consentManagement"], + "amp": ["consentManagement"], + "reactnative": ["consentManagement"], + "flutter": ["consentManagement"], + "cordova": ["consentManagement"], + "shopify": ["consentManagement"] }, "secretKeys": ["password", "accessKeyID", "accessKey", "accountKey", "sasToken"] } diff --git a/src/configurations/destinations/snowflake/schema.json b/src/configurations/destinations/snowflake/schema.json index b7e6f1da6..993dec98c 100644 --- a/src/configurations/destinations/snowflake/schema.json +++ b/src/configurations/destinations/snowflake/schema.json @@ -91,6 +91,425 @@ } } } + }, + "consentManagement": { + "type": "object", + "properties": { + "android": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "ios": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "web": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "unity": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "amp": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "reactnative": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "flutter": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "cordova": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + }, + "shopify": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["custom", "ketch", "oneTrust"], + "default": "oneTrust" + }, + "consents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "consent": { + "type": "string", + "pattern": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$" + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "custom" + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "resolutionStrategy": { + "type": "string", + "enum": ["and", "or"] + } + }, + "required": ["resolutionStrategy"] + } + } + ] + } + } + } } }, "allOf": [ diff --git a/src/configurations/destinations/snowflake/ui-config.json b/src/configurations/destinations/snowflake/ui-config.json index 3feccaa0c..15992e650 100644 --- a/src/configurations/destinations/snowflake/ui-config.json +++ b/src/configurations/destinations/snowflake/ui-config.json @@ -561,7 +561,19 @@ "label": "Category ID", "required": false } - ] + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } }, { "type": "dynamicCustomForm", @@ -576,7 +588,98 @@ "regex": "^(.{0,100})$", "required": false } - ] + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": false + }, + { + "configKey": "AMP_enable-gcm" + } + ], + "featureFlagsCondition": "or" + } + }, + { + "type": "dynamicCustomForm", + "value": "consentManagement", + "label": "Consent management settings", + "footerNote": "The support for category names is deprecated. We recommend using the category IDs instead of the names as IDs are unique and less likely to change over time, making them a more reliable choice.", + "customFields": [ + { + "type": "singleSelect", + "label": "Consent management provider", + "value": "provider", + "options": [ + { + "name": "Custom", + "value": "custom" + }, + { + "name": "Ketch", + "value": "ketch" + }, + { + "name": "OneTrust", + "value": "oneTrust" + } + ], + "defaultOption": { + "name": "OneTrust", + "value": "oneTrust" + }, + "required": true + }, + { + "type": "singleSelect", + "label": "the required consent logic", + "value": "resolutionStrategy", + "options": [ + { + "name": "AND", + "value": "and" + }, + { + "name": "OR", + "value": "or" + } + ], + "required": true, + "variant": "badge", + "preRequisites": { + "fields": [ + { + "configKey": "provider", + "value": "custom" + } + ] + } + }, + { + "type": "dynamicCustomForm", + "value": "consents", + "label": "Enter consent category ID’s", + "customFields": [ + { + "type": "textInput", + "placeholder": "Marketing", + "value": "consent", + "regex": "(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$", + "required": false + } + ] + } + ], + "preRequisites": { + "featureFlags": [ + { + "configKey": "AMP_enable-gcm", + "value": true + } + ] + } } ] } diff --git a/test/configData/ui-config.json b/test/configData/ui-config.json index 2c2ecef16..84c8a7207 100644 --- a/test/configData/ui-config.json +++ b/test/configData/ui-config.json @@ -134,6 +134,13 @@ ] } ] + }, + { + "id": "consentSettings", + "title": "Consent settings", + "note": "Configure consent settings for each provider here", + "icon": "settings", + "groups": [] } ] } @@ -142,6 +149,11 @@ "title": "Web SDK settings", "note": "not visible in the ui", "fields": [] + }, + "consentSettingsTemplate": { + "title": "Consent settings", + "note": "not visible in the ui", + "fields": [] } } } diff --git a/test/data/validation/destinations/customerio.json b/test/data/validation/destinations/customerio.json index b4c248a94..2795eae0c 100644 --- a/test/data/validation/destinations/customerio.json +++ b/test/data/validation/destinations/customerio.json @@ -185,5 +185,108 @@ "err": [ "deviceTokenEventName must match pattern \"(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(.{0,100})$\"" ] + }, + { + "testTitle": "With valid multiple consent management providers config", + "config": { + "siteID": "95bd1331112976i0ff9b", + "apiKey": "95bd1330072974f0ff9b", + "consentManagement": { + "web": [ + { + "provider": "custom", + "consents": [ + { + "consent": "Marketing" + } + ], + "resolutionStrategy": "or" + }, + { + "provider": "oneTrust", + "consents": [ + { + "consent": "Marketing" + } + ] + }, + { + "provider": "ketch", + "consents": [] + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config and invalid resolutionStrategy value", + "config": { + "siteID": "95bd1331112976i0ff9b", + "apiKey": "95bd1330072974f0ff9b", + "consentManagement": { + "android": [ + { + "provider": "custom", + "resolutionStrategy": "nor" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0.resolutionStrategy must be equal to one of the allowed values", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management custom provider config and no resolutionStrategy value", + "config": { + "siteID": "95bd1331112976i0ff9b", + "apiKey": "95bd1330072974f0ff9b", + "consentManagement": { + "android": [ + { + "provider": "custom" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0 must have required property 'resolutionStrategy'", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management OneTrust provider config and no resolutionStrategy value", + "config": { + "siteID": "95bd1331112976i0ff9b", + "apiKey": "95bd1330072974f0ff9b", + "consentManagement": { + "android": [ + { + "provider": "oneTrust" + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config invalid provider value", + "config": { + "siteID": "95bd1331112976i0ff9b", + "apiKey": "95bd1330072974f0ff9b", + "consentManagement": { + "android": [ + { + "provider": "dummyProvider" + } + ] + } + }, + "result": false, + "err": ["consentManagement.android.0.provider must be equal to one of the allowed values"] } ] diff --git a/test/data/validation/destinations/ga4.json b/test/data/validation/destinations/ga4.json index e3db15dc0..74584e071 100644 --- a/test/data/validation/destinations/ga4.json +++ b/test/data/validation/destinations/ga4.json @@ -324,5 +324,140 @@ ] }, "result": true + }, + { + "testTitle": "With valid multiple consent management providers config", + "config": { + "apiSecret": "QyWIGHj8QhG2L4ePAPiXCA", + "consentManagement": { + "web": [ + { + "provider": "custom", + "consents": [ + { + "consent": "Marketing" + } + ], + "resolutionStrategy": "or" + }, + { + "provider": "oneTrust", + "consents": [ + { + "consent": "Marketing" + } + ] + }, + { + "provider": "ketch", + "consents": [] + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config and invalid resolutionStrategy value", + "config": { + "apiSecret": "QyWIGHj8QhG2L4ePAPiXCA", + "consentManagement": { + "android": [ + { + "provider": "custom", + "resolutionStrategy": "nor" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0.resolutionStrategy must be equal to one of the allowed values", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management custom provider config and no resolutionStrategy value", + "config": { + "apiSecret": "QyWIGHj8QhG2L4ePAPiXCA", + "consentManagement": { + "android": [ + { + "provider": "custom" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0 must have required property 'resolutionStrategy'", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management OneTrust provider config and no resolutionStrategy value", + "config": { + "apiSecret": "QyWIGHj8QhG2L4ePAPiXCA", + "consentManagement": { + "android": [ + { + "provider": "oneTrust" + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config invalid provider value", + "config": { + "apiSecret": "QyWIGHj8QhG2L4ePAPiXCA", + "consentManagement": { + "android": [ + { + "provider": "dummyProvider" + } + ] + } + }, + "result": false, + "err": ["consentManagement.android.0.provider must be equal to one of the allowed values"] + }, + { + "config": { + "apiSecret": "QyWIGHj8QhG2L4ePAPiXCA", + "debugMode": false, + "typesOfClient": "gtag", + "measurementId": "G-adfg", + "firebaseAppId": "", + "whitelistedEvents": [{ "eventName": "" }], + "blacklistedEvents": [{ "eventName": "" }], + "eventFilteringOption": "disable", + "piiPropertiesToIgnore": [{ "piiProperty": "" }], + "oneTrustCookieCategories": [{ "oneTrustCookieCategory": "" }], + "ketchConsentPurposes": [{ "purpose": "" }], + "consentManagement": { + "web": [ + { + "provider": "custom", + "resolutionStrategy": "or", + "consents": [{ "consent": "vjvh" }, { "consent": "hkvhkv" }, { "consent": "jhvjh" }] + }, + { + "provider": "oneTrust", + "resolutionStrategy": "and", + "consents": [{ "consent": "mvhvjv" }, { "consent": "jvjvjv" }] + } + ] + }, + "debugView": { "web": true }, + "useNativeSDK": { "web": false }, + "connectionMode": { "web": "cloud" }, + "capturePageView": { "web": "rs" }, + "useNativeSDKToSend": { "web": false }, + "extendPageViewParams": { "web": false }, + "overrideClientAndSessionId": { "web": true } + }, + "result": true } ] diff --git a/test/data/validation/destinations/mp.json b/test/data/validation/destinations/mp.json index 658ba1bb1..90d3d9ff1 100644 --- a/test/data/validation/destinations/mp.json +++ b/test/data/validation/destinations/mp.json @@ -214,5 +214,103 @@ "ignoreDnt must be boolean", "persistenceType must be equal to one of the allowed values" ] + }, + { + "testTitle": "With valid multiple consent management providers config", + "config": { + "token": "2de18c6hf6v45201ab43d2344b0c128x", + "consentManagement": { + "web": [ + { + "provider": "custom", + "consents": [ + { + "consent": "Marketing" + } + ], + "resolutionStrategy": "or" + }, + { + "provider": "oneTrust", + "consents": [ + { + "consent": "Marketing" + } + ] + }, + { + "provider": "ketch", + "consents": [] + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config and invalid resolutionStrategy value", + "config": { + "token": "2de18c6hf6v45201ab43d2344b0c128x", + "consentManagement": { + "android": [ + { + "provider": "custom", + "resolutionStrategy": "nor" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0.resolutionStrategy must be equal to one of the allowed values", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management custom provider config and no resolutionStrategy value", + "config": { + "token": "2de18c6hf6v45201ab43d2344b0c128x", + "consentManagement": { + "android": [ + { + "provider": "custom" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0 must have required property 'resolutionStrategy'", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management OneTrust provider config and no resolutionStrategy value", + "config": { + "token": "2de18c6hf6v45201ab43d2344b0c128x", + "consentManagement": { + "android": [ + { + "provider": "oneTrust" + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config invalid provider value", + "config": { + "token": "2de18c6hf6v45201ab43d2344b0c128x", + "consentManagement": { + "android": [ + { + "provider": "dummyProvider" + } + ] + } + }, + "result": false, + "err": ["consentManagement.android.0.provider must be equal to one of the allowed values"] } ] diff --git a/test/data/validation/destinations/snowflake.json b/test/data/validation/destinations/snowflake.json index be514ee0f..e99290d79 100644 --- a/test/data/validation/destinations/snowflake.json +++ b/test/data/validation/destinations/snowflake.json @@ -381,5 +381,123 @@ "containerName must match pattern \"(^\\{\\{.*\\|\\|(.*)\\}\\}$)|(^env[.].+)|^(?=.{3,63}$)[a-z0-9]+(-[a-z0-9]+)*$\"", " must match \"then\" schema" ] + }, + { + "testTitle": "With valid multiple consent management providers config", + "config": { + "account": "test-account", + "database": "test-database", + "warehouse": "test-warehouse", + "user": "test-user", + "password": "test-password", + "consentManagement": { + "web": [ + { + "provider": "custom", + "consents": [ + { + "consent": "Marketing" + } + ], + "resolutionStrategy": "or" + }, + { + "provider": "oneTrust", + "consents": [ + { + "consent": "Marketing" + } + ] + }, + { + "provider": "ketch", + "consents": [] + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config and invalid resolutionStrategy value", + "config": { + "account": "test-account", + "database": "test-database", + "warehouse": "test-warehouse", + "user": "test-user", + "password": "test-password", + "consentManagement": { + "android": [ + { + "provider": "custom", + "resolutionStrategy": "nor" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0.resolutionStrategy must be equal to one of the allowed values", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management custom provider config and no resolutionStrategy value", + "config": { + "account": "test-account", + "database": "test-database", + "warehouse": "test-warehouse", + "user": "test-user", + "password": "test-password", + "consentManagement": { + "android": [ + { + "provider": "custom" + } + ] + } + }, + "result": false, + "err": [ + "consentManagement.android.0 must have required property 'resolutionStrategy'", + "consentManagement.android.0 must match \"then\" schema" + ] + }, + { + "testTitle": "With consent management OneTrust provider config and no resolutionStrategy value", + "config": { + "account": "test-account", + "database": "test-database", + "warehouse": "test-warehouse", + "user": "test-user", + "password": "test-password", + "consentManagement": { + "android": [ + { + "provider": "oneTrust" + } + ] + } + }, + "result": true + }, + { + "testTitle": "With consent management custom provider config invalid provider value", + "config": { + "account": "test-account", + "database": "test-database", + "warehouse": "test-warehouse", + "user": "test-user", + "password": "test-password", + "consentManagement": { + "android": [ + { + "provider": "dummyProvider" + } + ] + } + }, + "result": false, + "err": ["consentManagement.android.0.provider must be equal to one of the allowed values"] } ] diff --git a/test/validation.test.ts b/test/validation.test.ts index 12e4f0ff1..86c37691c 100644 --- a/test/validation.test.ts +++ b/test/validation.test.ts @@ -19,12 +19,12 @@ command const cmdOpts = command.opts(); -function getIntegrationNames(type) { +function getIntegrationNames(type: string) { const dirPath = path.resolve(`src/configurations/${type}`); return fs.readdirSync(dirPath).filter((file) => fs.statSync(`${dirPath}/${file}`).isDirectory()); } -function getIntegrationData(name, type): Record[] { +function getIntegrationData(name: string, type: string): Record[] { // eslint-disable-next-line @typescript-eslint/no-explicit-any let intgData: any; try { @@ -120,7 +120,7 @@ describe('Validation Tests', () => { Object.keys(destTcData).forEach((dest: string, destIdx: number) => { describe(`${destIdx + 1}. Destination - ${dest}`, () => { destTcData[dest].forEach((td: Record, tcIdx: number) => { - it(`TC ${tcIdx + 1}`, () => { + it(`TC ${tcIdx + 1}${td.testTitle ? ` - ${td.testTitle}` : ''}`, async () => { if (td.result === true) { expect( validateConfig(dest, td.config as Record, 'destinations', true), @@ -139,7 +139,7 @@ describe('Validation Tests', () => { Object.keys(srcTcData).forEach((src: string, srcIdx: number) => { describe(`${srcIdx + 1}. Source - ${src}`, () => { srcTcData[src].forEach((td: Record, tcIdx: number) => { - it(`TC ${tcIdx + 1}`, () => { + it(`TC ${tcIdx + 1}${td.testTitle ? ` - ${td.testTitle}` : ''}`, async () => { if (td.result === true) { expect( validateConfig(src, td.config as Record, 'sources', true),