diff --git a/.github/workflows/run-tests-bundled-esm.yml b/.github/workflows/run-tests-bundled-esm.yml index 134cc67f..d2f65117 100644 --- a/.github/workflows/run-tests-bundled-esm.yml +++ b/.github/workflows/run-tests-bundled-esm.yml @@ -25,5 +25,6 @@ jobs: run: npm run test:bundled:esm:parallel env: TEST_REQUEST_API_KEY: ${{ secrets.TEST_REQUEST_API_KEY }} + TEST_PIA_REQUEST_API_KEY: ${{ secrets.TEST_PIA_REQUEST_API_KEY }} TEST_MEDIA_REQUEST_API_KEY: ${{ secrets.TEST_MEDIA_REQUEST_API_KEY }} SKIP_NETWORK_TIMEOUT_TESTS: true diff --git a/cspell.json b/cspell.json index df8b430d..2653f568 100644 --- a/cspell.json +++ b/cspell.json @@ -50,6 +50,7 @@ "Bytespider", "Timespans", "googlequicksearchbox", - "cnstrc" + "cnstrc", + "iife" ] } diff --git a/spec/src/constructorio.js b/spec/src/constructorio.js index 4fdbcac2..989a794b 100644 --- a/spec/src/constructorio.js +++ b/spec/src/constructorio.js @@ -715,6 +715,105 @@ describe(`ConstructorIO${bundledDescriptionSuffix}`, () => { expect(instance.recommendations.options).to.have.property('sessionId').to.equal(oldSessionId); expect(instance.tracker.options).to.have.property('sessionId').to.equal(oldSessionId); }); + + it('Should update the client options with a new service url', () => { + const newServiceUrl = 'https://new-service-url.cnstrc.com'; + const instance = new ConstructorIO({ apiKey: validApiKey }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + + instance.setClientOptions({ + serviceUrl: newServiceUrl, + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + }); + + it('Should update the options for modules with a new service url', () => { + const newServiceUrl = 'https://new-service-url.cnstrc.com'; + const instance = new ConstructorIO({ apiKey: validApiKey }); + + expect(instance.search.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.autocomplete.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.browse.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.recommendations.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.tracker.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + + instance.setClientOptions({ + serviceUrl: newServiceUrl, + }); + + expect(instance.search.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.autocomplete.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.browse.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.recommendations.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.tracker.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + }); + + it('Should not update the client options service url with a falsy value', () => { + const originalServiceUrl = 'https://custom-service-url.cnstrc.com'; + const instance = new ConstructorIO({ + apiKey: validApiKey, + serviceUrl: originalServiceUrl, + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); + + instance.setClientOptions({ + serviceUrl: '', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); + + instance.setClientOptions({ + serviceUrl: null, + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); + }); + + it('Should prepend https to a service url without a protocol', () => { + const instance = new ConstructorIO({ apiKey: validApiKey }); + + instance.setClientOptions({ + serviceUrl: 'new-service-url.cnstrc.com', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://new-service-url.cnstrc.com'); + }); + + it('Should strip a trailing slash from the service url', () => { + const instance = new ConstructorIO({ apiKey: validApiKey }); + + instance.setClientOptions({ + serviceUrl: 'https://new-service-url.cnstrc.com/', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://new-service-url.cnstrc.com'); + }); + + it('Should upgrade an http service url to https by default', () => { + const instance = new ConstructorIO({ apiKey: validApiKey }); + + instance.setClientOptions({ + serviceUrl: 'http://new-service-url.cnstrc.com', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://new-service-url.cnstrc.com'); + }); + + it('Should preserve an http service url when allowHttpServiceUrl is set', () => { + const instance = new ConstructorIO({ + apiKey: validApiKey, + allowHttpServiceUrl: true, + }); + + instance.setClientOptions({ + serviceUrl: 'http://new-service-url.cnstrc.com', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('http://new-service-url.cnstrc.com'); + }); }); if (bundled) { diff --git a/src/constructorio.js b/src/constructorio.js index c4e8cb39..7572724c 100644 --- a/src/constructorio.js +++ b/src/constructorio.js @@ -128,6 +128,7 @@ class ConstructorIO { apiKey, version: versionFromOptions || versionFromGlobal || computePackageVersion(), serviceUrl: helpers.addHTTPSToString(normalizedServiceUrl, allowHttpServiceUrl) || 'https://ac.cnstrc.com', + allowHttpServiceUrl, quizzesServiceUrl: (quizzesServiceUrl && quizzesServiceUrl.replace(/\/$/, '')) || 'https://quizzes.cnstrc.com', agentServiceUrl: (agentServiceUrl && agentServiceUrl.replace(/\/$/, '')) || 'https://agent.cnstrc.com', assistantServiceUrl: (assistantServiceUrl && assistantServiceUrl.replace(/\/$/, '')) || 'https://assistant.cnstrc.com', @@ -177,10 +178,11 @@ class ConstructorIO { * @param {number} [options.sessionId] - Session ID - Will only be set in DOM-less environments * @param {string} [options.userId] - User ID * @param {boolean} [options.sendTrackingEvents] - Indicates if tracking events should be dispatched + * @param {string} [options.serviceUrl] - API URL endpoint (normalized to include an HTTPS protocol and strip a trailing slash) */ setClientOptions(options) { if (Object.keys(options).length) { - const { apiKey, segments, testCells, sessionId, userId, sendTrackingEvents } = options; + const { apiKey, segments, testCells, sessionId, userId, sendTrackingEvents, serviceUrl } = options; if (apiKey) { this.options.apiKey = apiKey; @@ -206,6 +208,12 @@ class ConstructorIO { if ('userId' in options) { this.options.userId = userId; } + + if (typeof serviceUrl === 'string' && serviceUrl.length) { + const normalizedServiceUrl = serviceUrl.replace(/\/$/, ''); + const formattedServiceUrl = helpers.addHTTPSToString(normalizedServiceUrl, this.options.allowHttpServiceUrl); + this.options.serviceUrl = formattedServiceUrl || this.options.serviceUrl; + } } } }