diff --git a/.changeset/chilly-houses-rule.md b/.changeset/chilly-houses-rule.md new file mode 100644 index 0000000..f2cdbde --- /dev/null +++ b/.changeset/chilly-houses-rule.md @@ -0,0 +1,5 @@ +--- +"@stackla/widget-utils": patch +--- + +Add support for versioning diff --git a/src/embed/embed.spec.ts b/src/embed/embed.spec.ts index e912e39..6c52c8c 100644 --- a/src/embed/embed.spec.ts +++ b/src/embed/embed.spec.ts @@ -1,5 +1,5 @@ /* eslint-disable no-var */ -import { embed } from "." +import { embed, JSONSchema } from "." import fetchMock from "jest-fetch-mock" import { getWidgetV2EmbedCode } from "./v2" import { getWidgetV3EmbedCode } from "./v3" @@ -7,8 +7,18 @@ import { generateDataHTMLStringByParams } from "./embed.params" fetchMock.enableMocks() -const REQUEST_URL = "https://widget-data.stackla.com/widgets/123/version" -const STAGING_REQUEST_URL = "https://widget-data.teaser.stackla.com/widgets/123/version" +const REQUEST_URL = "https://widget-data.stackla.com/widgets/123/version/" +const STAGING_REQUEST_URL = "https://widget-data.teaser.stackla.com/widgets/123/version/" + +const V2Request: JSONSchema = { + widgetVersion: 2, + scriptVersion: 1 +} + +const V3Request: JSONSchema = { + widgetVersion: 3, + scriptVersion: 1 +} describe("load embed code", () => { beforeEach(() => { @@ -17,7 +27,7 @@ describe("load embed code", () => { it("should return the correct embed code for v2", async () => { fetchMock.mockIf(REQUEST_URL, async () => { - return JSON.stringify({ version: 2 }) + return JSON.stringify(V2Request) }) const createdDiv = document.createElement("div") @@ -37,7 +47,7 @@ describe("load embed code", () => { it("should test staging for v2", async () => { fetchMock.mockIf(STAGING_REQUEST_URL, async () => { - return JSON.stringify({ version: 2 }) + return JSON.stringify(V2Request) }) const createdDiv = document.createElement("div") @@ -56,7 +66,7 @@ describe("load embed code", () => { it("should test production for v2", async () => { fetchMock.mockIf(REQUEST_URL, async () => { - return JSON.stringify({ version: 2 }) + return JSON.stringify(V2Request) }) const createdDiv = document.createElement("div") @@ -75,7 +85,7 @@ describe("load embed code", () => { it("should return the correct embed code for v3", async () => { fetchMock.mockIf(REQUEST_URL, async () => { - return JSON.stringify({ version: 3 }) + return JSON.stringify(V3Request) }) const createdDiv = document.createElement("div") @@ -114,24 +124,6 @@ describe("load embed code", () => { } }) - it("should skip the fetch call if the version is provided", async () => { - const createdDiv = document.createElement("div") - await embed({ - widgetId: "123", - root: createdDiv, - version: 3, - dataProperties: { - foo: "bar", - baz: 123 - }, - environment: "production" - }) - - expect(fetchMock).not.toHaveBeenCalled() - expect(createdDiv.innerHTML).toContain(getWidgetV3EmbedCode({ foo: "bar", baz: 123, wid: "123" })) - expect(createdDiv.innerHTML).toContain('
') - }) - it("should test param string method", async () => { const params = generateDataHTMLStringByParams({ foo: "bar", baz: 123, wid: "123" }) @@ -139,11 +131,14 @@ describe("load embed code", () => { }) it("should deal with malicious payloads", async () => { + fetchMock.mockIf(REQUEST_URL, async () => { + return JSON.stringify(V3Request) + }) + const createdDiv = document.createElement("div") await embed({ widgetId: "123", root: createdDiv, - version: 3, dataProperties: { foo: "bar", baz: 123, @@ -152,7 +147,6 @@ describe("load embed code", () => { environment: "production" }) - expect(fetchMock).not.toHaveBeenCalled() expect(createdDiv.innerHTML).toContain( `
` ) diff --git a/src/embed/index.ts b/src/embed/index.ts index ddb7c3f..aa41423 100644 --- a/src/embed/index.ts +++ b/src/embed/index.ts @@ -8,18 +8,17 @@ import { getWidgetV2EmbedCode, invokeV2Javascript } from "./v2" import { getWidgetV3EmbedCode, invokeV3Javascript } from "./v3" export type Environment = "staging" | "production" -type Generation = 2 | 3 interface EmbedOptions { widgetId: string root: T environment: Environment - version?: Generation dataProperties: Record } -interface JSONSchema { - version: number +export interface JSONSchema { + widgetVersion: number + scriptVersion: number } export function getWidgetDataUrl(env: Environment) { @@ -41,14 +40,12 @@ export function getLegacyWidgetDomain(env: Environment) { } function getRequestUrl(widgetId: string, environment: Environment) { - return `${getWidgetDataUrl(environment)}/widgets/${widgetId}/version` + return `${getWidgetDataUrl(environment)}/widgets/${widgetId}/version/` } -async function retrieveWidgetVersionFromServer(widgetId: string, environment: Environment): Promise { +async function retrieveWidgetMetaFromServer(widgetId: string, environment: Environment): Promise { const response = await fetch(getRequestUrl(widgetId, environment)) - const json: JSONSchema = await response.json() - - return json.version + return response.json() } function injectHTML(root: HTMLElement | ShadowRoot, html: string) { @@ -56,10 +53,11 @@ function injectHTML(root: HTMLElement | ShadowRoot, html: string) { } export async function embed(options: EmbedOptions) { - const { environment, widgetId, root, version, dataProperties } = options + const { environment, widgetId, root, dataProperties } = options try { - const widgetVersion = version ?? (await retrieveWidgetVersionFromServer(widgetId, environment)) + const widgetMeta = await retrieveWidgetMetaFromServer(widgetId, environment) + const { widgetVersion, scriptVersion } = widgetMeta switch (widgetVersion) { case 2: window.stackWidgetDomain = getLegacyWidgetDomain(environment) @@ -70,7 +68,7 @@ export async function embed(options: EmbedOp case 3: dataProperties["wid"] = widgetId injectHTML(root, getWidgetV3EmbedCode(dataProperties)) - await invokeV3Javascript(environment) + await invokeV3Javascript(environment, scriptVersion) break default: throw new Error(`No widget code accessible with version ${widgetVersion}`) diff --git a/src/embed/v3.ts b/src/embed/v3.ts index 2fb6a81..c23f77f 100644 --- a/src/embed/v3.ts +++ b/src/embed/v3.ts @@ -18,8 +18,8 @@ const getWidgetV3EmbedCode = (data: Record) = return `
` } -const invokeV3Javascript = async (environment: Environment) => { - const widget = await import(`${getUrlByEnv(environment)}/core.esm.js`) +const invokeV3Javascript = async (environment: Environment, scriptVersion: number) => { + const widget = await import(`${getUrlByEnv(environment)}/v${scriptVersion}/core.esm.js`) widget.init() }