diff --git a/src/DataProvider.js b/src/DataProvider.js index ae64c64c..4011fac6 100644 --- a/src/DataProvider.js +++ b/src/DataProvider.js @@ -7,6 +7,15 @@ import i18next from "i18next"; const FIVE_MINUTES = 1000 * 60 * 5; +function normalizeDataSourceId(dataSourceId) { + const match = + typeof dataSourceId === "string" + ? dataSourceId.match(/^data_source-(\d+)$/) + : null; + + return match ? match[1] : dataSourceId; +} + export default { screensCache: [], cachedScreenPromises: [], @@ -196,12 +205,13 @@ export default { ); }, - postDataSource(scriptId, requestId, params) { + postDataSource(dataSourceId, requestId, params) { + const normalizedDataSourceId = normalizeDataSourceId(dataSourceId); let url; if (requestId) { - url = `/requests/${requestId}/data_sources/${scriptId}`; + url = `/requests/${requestId}/data_sources/${normalizedDataSourceId}`; } else { - url = `/requests/data_sources/${scriptId}`; + url = `/requests/data_sources/${normalizedDataSourceId}`; } url += this.authQueryString(); @@ -215,14 +225,20 @@ export default { * @returns {object} */ getDataSource(dataSourceId, params, nonce = null) { + const normalizedDataSourceId = normalizeDataSourceId(dataSourceId); // keep backwards compatibility if ( !window.ProcessMaker.screen.cacheEnabled && !window.ProcessMaker.screen.cacheTimeout ) { - return this.postDataSource(dataSourceId, null, params).then(r => [r, nonce]); + return this.postDataSource(dataSourceId, null, params).then((r) => [ + r, + nonce + ]); } - let url = `/requests/data_sources/${dataSourceId}/resources/${params.config.endpoint}/data`; + let url = + `/requests/data_sources/${normalizedDataSourceId}` + + `/resources/${params.config.endpoint}/data`; url += this.authQueryString(); return this.get(url, { useCache: window.ProcessMaker.screen.cacheEnabled, diff --git a/tests/e2e/fixtures/FOUR-6910_Watcher_Radio.json b/tests/e2e/fixtures/FOUR-6910_Watcher_Radio.json index 7bd856ed..56e1adbc 100644 --- a/tests/e2e/fixtures/FOUR-6910_Watcher_Radio.json +++ b/tests/e2e/fixtures/FOUR-6910_Watcher_Radio.json @@ -1146,7 +1146,7 @@ "title": "Students", "key": "package-data-sources\/data-source-task-service" }, - "script_id": "2", + "script_id": "data_source-2", "script_key": "package-data-sources\/data-source-task-service", "uid": "16667071052851" } @@ -1204,4 +1204,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/e2e/specs/SelectListWatcher.spec.js b/tests/e2e/specs/SelectListWatcher.spec.js index cbab482c..2a1c3c7b 100644 --- a/tests/e2e/specs/SelectListWatcher.spec.js +++ b/tests/e2e/specs/SelectListWatcher.spec.js @@ -54,6 +54,8 @@ describe("SelectList - Watcher", () => { '[data-cy=preview-content] [id^="form_select_list_2-John-"]' ).click(); + cy.wait("@getWatcherResponse"); + // Select "Mary" option in select list "form_select_list_3" cy.get( "[data-cy=preview-content] [data-cy=screen-field-form_select_list_3]" diff --git a/tests/unit/DataProvider.spec.js b/tests/unit/DataProvider.spec.js new file mode 100644 index 00000000..c2bd9a02 --- /dev/null +++ b/tests/unit/DataProvider.spec.js @@ -0,0 +1,137 @@ +import DataProvider from "@/DataProvider"; + +jest.mock("axios-extensions", () => ({ + cacheAdapterEnhancer: jest.fn() +})); + +describe("DataProvider data source IDs", () => { + const params = { + config: { endpoint: "GetRecord" }, + data: { employee_col_id: 20001 } + }; + + beforeEach(() => { + window.ProcessMaker = { + screen: { + cacheEnabled: false, + cacheTimeout: 0 + } + }; + delete window.PM4ConfigOverrides; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test.each([ + ["data_source-7", null, "/requests/data_sources/7"], + ["data_source-007", null, "/requests/data_sources/007"], + ["data_source-7", 123, "/requests/123/data_sources/7"], + [7, null, "/requests/data_sources/7"], + ["7", null, "/requests/data_sources/7"], + ["data_source-abc", null, "/requests/data_sources/data_source-abc"], + [ + "data_source-7-extra", + null, + "/requests/data_sources/data_source-7-extra" + ], + ["data_source-", null, "/requests/data_sources/data_source-"], + [ + "data_source-data_source-7", + null, + "/requests/data_sources/data_source-data_source-7" + ], + [ + "custom-data_source-7", + null, + "/requests/data_sources/custom-data_source-7" + ] + ])( + "posts data source %p with request %p to %s", + async (dataSourceId, requestId, expectedUrl) => { + const response = { data: { employee: "QA Employee 20001" } }; + const post = jest.spyOn(DataProvider, "post").mockResolvedValue(response); + + await expect( + DataProvider.postDataSource(dataSourceId, requestId, params) + ).resolves.toBe(response); + + expect(post).toHaveBeenCalledWith(expectedUrl, params, { timeout: 0 }); + } + ); + + test("preserves authentication parameters after normalizing the ID", async () => { + window.PM4ConfigOverrides = { + authParams: { token: "preview-token" } + }; + const post = jest.spyOn(DataProvider, "post").mockResolvedValue({}); + + await DataProvider.postDataSource("data_source-7", null, params); + + expect(post).toHaveBeenCalledWith( + "/requests/data_sources/7?token=preview-token", + params, + { timeout: 0 } + ); + }); + + test.each([ + ["data_source-7", "/requests/data_sources/7"], + [ + "data_source-data_source-7", + "/requests/data_sources/data_source-data_source-7" + ] + ])( + "passes the original ID %p through the non-cached fallback", + async (dataSourceId, expectedUrl) => { + const response = { data: { data: [] } }; + const postDataSource = jest.spyOn(DataProvider, "postDataSource"); + const post = jest.spyOn(DataProvider, "post").mockResolvedValue(response); + + await expect( + DataProvider.getDataSource(dataSourceId, params, "request-nonce") + ).resolves.toEqual([response, "request-nonce"]); + + expect(postDataSource).toHaveBeenCalledWith(dataSourceId, null, params); + expect(post).toHaveBeenCalledWith(expectedUrl, params, { timeout: 0 }); + } + ); + + test.each([ + ["data_source-7", "/requests/data_sources/7/resources/GetRecord/data"], + [ + "data_source-007", + "/requests/data_sources/007/resources/GetRecord/data" + ], + [ + "data_source-abc", + "/requests/data_sources/data_source-abc/resources/GetRecord/data" + ], + [ + "data_source-data_source-7", + "/requests/data_sources/data_source-data_source-7/resources/GetRecord/data" + ], + [7, "/requests/data_sources/7/resources/GetRecord/data"] + ])( + "uses data source %p consistently on the cached GET path", + async (dataSourceId, expectedUrl) => { + window.ProcessMaker.screen.cacheEnabled = true; + window.ProcessMaker.screen.cacheTimeout = 300000; + const response = { data: { data: [] } }; + const get = jest.spyOn(DataProvider, "get").mockResolvedValue(response); + + await expect( + DataProvider.getDataSource(dataSourceId, params, "request-nonce") + ).resolves.toEqual([response, "request-nonce"]); + + expect(get).toHaveBeenCalledWith(expectedUrl, { + useCache: true, + params: { + pmds_config: JSON.stringify(params.config), + pmds_data: JSON.stringify(params.data) + } + }); + } + ); +});