diff --git a/src/__tests__/createGetDataElementValue.test.js b/src/__tests__/createGetDataElementValue.test.js index 3ff3f91..a04b7bf 100644 --- a/src/__tests__/createGetDataElementValue.test.js +++ b/src/__tests__/createGetDataElementValue.test.js @@ -27,12 +27,23 @@ const createGetDataElementDefinitionDefault = (dataDef) => return null; }); +const createGetModuleProvider = (o = {}) => { + return { + getModuleExports: () => (context) => context.utils.getSettings().foo, + getModuleDefinition: () => ({ + extensionName: 'someextension' + }), + getExtensionDefinition: () => ({ + getSettings: () => ({ data: 'extension settings' }) + }), + ...o + }; +}; + const createGetDataElementValue = ( delegateDefinition, { - moduleProvider = { - getModuleExports: () => (context) => context.utils.getSettings().foo - }, + moduleProvider = createGetModuleProvider(), createGetDataElementDefinition = createGetDataElementDefinitionDefault } = {} ) => { @@ -46,7 +57,7 @@ const createGetDataElementValue = ( }; describe('function returned by createGetDataElementValue', () => { - test('returns a value from the settings object as value', () => { + test('returns a value from the settings object as value', async () => { const getDataElementValue = createGetDataElementValue({ settings: { foo: 'bar' @@ -58,7 +69,7 @@ describe('function returned by createGetDataElementValue', () => { ); }); - test('returns a value from the contextData object as value', () => { + test('returns a value from the contextData object as value', async () => { const context = { arcAndUtils: { arc: { @@ -67,9 +78,9 @@ describe('function returned by createGetDataElementValue', () => { } }; - const moduleProvider = { + const moduleProvider = createGetModuleProvider({ getModuleExports: () => (c) => c.arc.foo - }; + }); const getDataElementValue = createGetDataElementValue( {}, @@ -81,7 +92,79 @@ describe('function returned by createGetDataElementValue', () => { ); }); - test('cleans the value when cleanText = true', () => { + test('gives extension settings access to the data element module', async () => { + const moduleProvider = createGetModuleProvider({ + getModuleExports: + () => + ({ utils }) => + utils.getExtensionSettings() + }); + + const getDataElementValue = createGetDataElementValue( + {}, + { moduleProvider } + ); + + return getDataElementValue('testDataElement', defaultContext).then( + (dataElementValue) => + expect(dataElementValue).toEqual({ data: 'extension settings' }) + ); + }); + + test('gives env access to the data element module called by the core extension', async () => { + const context = { + arcAndUtils: {}, + env: { + foo: 'bar' + } + }; + + const moduleProvider = createGetModuleProvider({ + getModuleDefinition: () => ({ + extensionName: 'core' + }), + getModuleExports: + () => + ({ utils }) => + utils.getEnv() + }); + + const getDataElementValue = createGetDataElementValue( + {}, + { moduleProvider } + ); + + return getDataElementValue('testDataElement', context).then( + (dataElementValue) => expect(dataElementValue).toEqual({ foo: 'bar' }) + ); + }); + + test('does not give env access to the data element module called by extensions that are not core', async () => { + const context = { + arcAndUtils: {}, + env: { + foo: 'bar' + } + }; + + const moduleProvider = createGetModuleProvider({ + getModuleExports: + () => + ({ utils }) => + utils.getEnv() + }); + + const getDataElementValue = createGetDataElementValue( + {}, + { moduleProvider } + ); + + return getDataElementValue('testDataElement', context).then( + (dataElementValue) => expect(dataElementValue).toEqual({}) + ); + }); + + test('cleans the value when cleanText = true', async () => { const getDataElementValue = createGetDataElementValue({ cleanText: true, settings: { foo: 'bar' } @@ -93,11 +176,11 @@ describe('function returned by createGetDataElementValue', () => { }); [undefined, null].forEach((testDataElementValue) => { - const moduleProvider = { + const moduleProvider = createGetModuleProvider({ getModuleExports: () => () => testDataElementValue - }; + }); - test(`returns a default value if data element value is ${testDataElementValue}`, () => { + test(`returns a default value if data element value is ${testDataElementValue}`, async () => { const getDataElementValue = createGetDataElementValue( { defaultValue: 'defaultValue', @@ -112,7 +195,7 @@ describe('function returned by createGetDataElementValue', () => { }); test(`returns ${testDataElementValue} if data element value is ${testDataElementValue} - and default is undefined`, () => { + and default is undefined`, async () => { const getDataElementValue = createGetDataElementValue( { settings: {} @@ -128,11 +211,11 @@ describe('function returned by createGetDataElementValue', () => { }); ['', 0, false, NaN].forEach((testDataElementValue) => { - const moduleProvider = { + const moduleProvider = createGetModuleProvider({ getModuleExports: () => () => testDataElementValue - }; + }); - test(`does not return a default value if value is ${testDataElementValue}`, () => { + test(`does not return a default value if value is ${testDataElementValue}`, async () => { const getDataElementValue = createGetDataElementValue( { defaultValue: 'defaultValue', @@ -148,7 +231,7 @@ describe('function returned by createGetDataElementValue', () => { }); }); - test('lowercases the value if forceLowerCase = true', () => { + test('lowercases the value if forceLowerCase = true', async () => { const getDataElementValue = createGetDataElementValue({ forceLowerCase: true, settings: { @@ -161,7 +244,7 @@ describe('function returned by createGetDataElementValue', () => { ); }); - test('lowercases the default value if forceLowerCase = true', () => { + test('lowercases the default value if forceLowerCase = true', async () => { const getDataElementValue = createGetDataElementValue({ forceLowerCase: true, defaultValue: 'bAr', @@ -173,12 +256,12 @@ describe('function returned by createGetDataElementValue', () => { ); }); - test('throws an error when calling data element module exports fails', () => { - const moduleProvider = { + test('throws an error when calling data element module exports fails', async () => { + const moduleProvider = createGetModuleProvider({ getModuleExports: () => () => { throw new Error('noob tried to divide by zero'); } - }; + }); const getDataElementValue = createGetDataElementValue( { @@ -195,12 +278,12 @@ describe('function returned by createGetDataElementValue', () => { }); }); - test('throws an error when calling data element module exports fails', () => { - const moduleProvider = { + test('throws an error when calling data element module exports fails', async () => { + const moduleProvider = createGetModuleProvider({ getModuleExports: () => () => { throw new Error('noob tried to divide by zero'); } - }; + }); const getDataElementValue = createGetDataElementValue( { @@ -221,7 +304,7 @@ describe('function returned by createGetDataElementValue', () => { }); }); - test('throws an error when data element definition is not found', () => { + test('throws an error when data element definition is not found', async () => { const getDataElementValue = createGetDataElementValue( { settings: {} @@ -240,7 +323,7 @@ describe('function returned by createGetDataElementValue', () => { }); }); - test('throws an error when data element circular reference is detected', () => { + test('throws an error when data element circular reference is detected', async () => { const getDataElementValue = createGetDataElementValue({ settings: { foo: 'bar' @@ -261,13 +344,13 @@ describe('function returned by createGetDataElementValue', () => { }); }); - test('provides access to getComponent method when it calls the data element module ', () => { - const moduleProvider = { + test('provides access to getComponent method when it calls the data element module ', async () => { + const moduleProvider = createGetModuleProvider({ getModuleExports: () => ({ utils: { getComponent } }) => getComponent() - }; + }); const getDataElementValue = createGetDataElementValue( { diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..414e1fe --- /dev/null +++ b/src/constants.js @@ -0,0 +1,12 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +module.exports = { CORE: 'core' }; diff --git a/src/createGetDataElementValue.js b/src/createGetDataElementValue.js index 5ab2e6e..ba5b5dc 100644 --- a/src/createGetDataElementValue.js +++ b/src/createGetDataElementValue.js @@ -10,6 +10,9 @@ governing permissions and limitations under the License. */ const cleanTextFn = require('./cleanText'); +const constants = require('./constants'); + +const { CORE } = constants; const enhanceErrorMessage = (dataElementName, e) => { e.message = `Failed to execute module for data element "${dataElementName}". ${e.message}`; @@ -17,7 +20,7 @@ const enhanceErrorMessage = (dataElementName, e) => { module.exports = (moduleProvider, getDataElementDefinition) => (dataElementName, context) => { - const { dataElementCallStack = [], arcAndUtils } = context; + const { dataElementCallStack = [], arcAndUtils, env } = context; const { utils } = arcAndUtils; const dataDef = getDataElementDefinition(dataElementName); @@ -54,14 +57,23 @@ module.exports = } = dataDef; const moduleExports = moduleProvider.getModuleExports(modulePath); - const valuePromise = getSettings(context).then((settings) => { + const moduleDefinition = moduleProvider.getModuleDefinition(modulePath); + const { getSettings: getExtensionSettings = () => Promise.resolve({}) } = + moduleProvider.getExtensionDefinition(modulePath); + + const valuePromise = Promise.all([ + getSettings(context), + getExtensionSettings(context) + ]).then(([settings, extensionSettings]) => { try { return moduleExports({ ...arcAndUtils, utils: { ...utils, getSettings: () => settings, - getComponent: () => ({ id, name }) + getExtensionSettings: () => extensionSettings, + getComponent: () => ({ id, name }), + getEnv: () => (moduleDefinition.extensionName === CORE ? env : {}) } }); } catch (e) { diff --git a/src/executeRules.js b/src/executeRules.js index 76117bc..ad887fd 100644 --- a/src/executeRules.js +++ b/src/executeRules.js @@ -24,14 +24,16 @@ module.exports = ( container, globalFetch, callData, + env, { headersForSubrequests } = {} ) => { const rulePromises = []; const { rules = [], - headerOverrides = [], - logSensitiveTokens = [], + + getHeaderOverrides = () => [], + getLogSensitiveTokens = () => [], buildInfo } = container; @@ -40,11 +42,14 @@ module.exports = ( rules.forEach((rule) => { const { id, name } = rule; - const logger = createNewLogger({ ruleId: rule.id }, logSensitiveTokens); + const logger = createNewLogger( + { ruleId: rule.id }, + getLogSensitiveTokens(env) + ); const fetch = getRuleFetchFn( globalFetch, - headerOverrides, + getHeaderOverrides(env), headersForSubrequests, logger ); @@ -57,6 +62,7 @@ module.exports = ( }; const initialContext = { + env, arcAndUtils: { utils, arc: { diff --git a/src/rules/__tests__/getExtensionSettingsByRuleComponent.test.js b/src/rules/__tests__/getUtilsByRuleComponent.test.js similarity index 61% rename from src/rules/__tests__/getExtensionSettingsByRuleComponent.test.js rename to src/rules/__tests__/getUtilsByRuleComponent.test.js index 8e069cb..7247b06 100644 --- a/src/rules/__tests__/getExtensionSettingsByRuleComponent.test.js +++ b/src/rules/__tests__/getUtilsByRuleComponent.test.js @@ -9,14 +9,14 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -const getExtensionSettingsByRuleComponent = require('../getExtensionSettingsByRuleComponent'); +const getUtilsByRuleComponent = require('../getUtilsByRuleComponent'); -describe('getExtensionSettingsByRuleComponent', () => { +describe('getUtilsByRuleComponent', () => { test('adds the extension settings to the context data', () => { const extensionSettings = { setting1: 1 }; const arcAndUtils = { arc: { contextData1: 2 }, utils: {} }; - return getExtensionSettingsByRuleComponent({ + return getUtilsByRuleComponent({ delegateConfig: { extension: { getExtensionSettings: () => Promise.resolve(extensionSettings) @@ -38,13 +38,59 @@ describe('getExtensionSettingsByRuleComponent', () => { }); }); + test('adds the env to the context data of the core extension', () => { + const arcAndUtils = {}; + + return getUtilsByRuleComponent({ + delegateConfig: { + extension: { + name: 'core' + } + }, + arcAndUtils, + env: { foo: 'bar' } + }).then((context) => { + const { + arcAndUtils: { + utils: { getEnv } + } + } = context; + + expect(getEnv()).toStrictEqual({ + foo: 'bar' + }); + }); + }); + + test('does not add the env to the context data of the core extension', () => { + const arcAndUtils = {}; + + return getUtilsByRuleComponent({ + delegateConfig: { + extension: { + name: 'another' + } + }, + arcAndUtils, + env: { foo: 'bar' } + }).then((context) => { + const { + arcAndUtils: { + utils: { getEnv } + } + } = context; + + expect(getEnv()).toStrictEqual({}); + }); + }); + test( 'adds and empty object as the extension settings to the context data if ' + 'getExtensionSetting is not provided', () => { const arcAndUtils = { arc: { contextData1: 2 }, utils: {} }; - return getExtensionSettingsByRuleComponent({ + return getUtilsByRuleComponent({ delegateConfig: { extension: {} }, diff --git a/src/rules/getExecuteModulePromise.js b/src/rules/getExecuteModulePromise.js index 48b8d93..282532e 100644 --- a/src/rules/getExecuteModulePromise.js +++ b/src/rules/getExecuteModulePromise.js @@ -11,12 +11,12 @@ governing permissions and limitations under the License. const logDelegateModuleCall = require('./logDelegateModuleCall'); const logDelegateModuleOutput = require('./logDelegateModuleOutput'); -const getExtensionSettingsByRuleComponent = require('./getExtensionSettingsByRuleComponent'); +const getUtilsByRuleComponent = require('./getUtilsByRuleComponent'); const executeDelegateModule = require('./executeDelegateModule'); module.exports = (context) => Promise.resolve(context) .then(logDelegateModuleCall) - .then(getExtensionSettingsByRuleComponent) + .then(getUtilsByRuleComponent) .then(executeDelegateModule) .then(logDelegateModuleOutput); diff --git a/src/rules/getExtensionSettingsByRuleComponent.js b/src/rules/getUtilsByRuleComponent.js similarity index 77% rename from src/rules/getExtensionSettingsByRuleComponent.js rename to src/rules/getUtilsByRuleComponent.js index 87cd045..bf7f349 100644 --- a/src/rules/getExtensionSettingsByRuleComponent.js +++ b/src/rules/getUtilsByRuleComponent.js @@ -8,14 +8,21 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +const constants = require('../constants'); + +const { CORE } = constants; module.exports = (context) => { - const { arcAndUtils, delegateConfig } = context; + const { arcAndUtils, delegateConfig, env = {} } = context; const { utils } = arcAndUtils; let { extension: { getExtensionSettings } } = delegateConfig; + const { + extension: { name: extensionName } + } = delegateConfig; + if (!getExtensionSettings) { getExtensionSettings = () => Promise.resolve({}); } @@ -25,9 +32,11 @@ module.exports = (context) => { ...arcAndUtils, utils: { ...utils, + getEnv: extensionName === CORE ? () => env : () => ({}), getExtensionSettings: () => extensionSettings } }, - delegateConfig + delegateConfig, + env })); };