From c0d36d2a1a7177aa8d79a4518e2b46bfbc0c1965 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Jan 2022 10:49:31 +0000 Subject: [PATCH] Add tests for REST pagination --- packages/server/src/integrations/rest.ts | 16 +- .../src/integrations/tests/rest.spec.js | 311 ++++++++++++++++-- 2 files changed, 293 insertions(+), 34 deletions(-) diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts index 9667400400..5c5c506704 100644 --- a/packages/server/src/integrations/rest.ts +++ b/packages/server/src/integrations/rest.ts @@ -218,13 +218,13 @@ module RestModule { } addBody(bodyType: string, body: string | any, input: any, pagination: PaginationConfig | null, paginationValues: PaginationValues | null) { - if (bodyType === BodyTypes.NONE) { - return input - } if (!input.headers) { input.headers = {} } - let error, object = {}, string = "" + if (bodyType === BodyTypes.NONE) { + return input + } + let error, object: any = {}, string = "" try { if (body) { string = typeof body !== "string" ? JSON.stringify(body) : body @@ -278,19 +278,15 @@ module RestModule { input.body = string input.headers["Content-Type"] = "application/xml" break - default: case BodyTypes.JSON: // if JSON error, throw it if (error) { throw "Invalid JSON for request body" } - if (!body) { - body = {} - } addPaginationToBody((key: string, value: any) => { - body[key] = value + object[key] = value }) - input.body = JSON.stringify(body) + input.body = JSON.stringify(object) input.headers["Content-Type"] = "application/json" break } diff --git a/packages/server/src/integrations/tests/rest.spec.js b/packages/server/src/integrations/tests/rest.spec.js index 6cfa670622..8f3c7f7f58 100644 --- a/packages/server/src/integrations/tests/rest.spec.js +++ b/packages/server/src/integrations/tests/rest.spec.js @@ -4,19 +4,23 @@ jest.mock("node-fetch", () => raw: () => { return { "content-type": ["application/json"] } }, - get: () => ["application/json"] + get: () => ["application/json"], }, - json: jest.fn(), - text: jest.fn() + json: jest.fn(() => ({ + my_next_cursor: 123, + })), + text: jest.fn(), })) ) const fetch = require("node-fetch") const RestIntegration = require("../rest") const { AuthType } = require("../rest") +const FormData = require("form-data") +const { URLSearchParams } = require("url") const HEADERS = { - "Accept": "application/json", - "Content-Type": "application/json" + Accept: "application/json", + "Content-Type": "application/json", } class TestConfiguration { @@ -165,17 +169,20 @@ describe("REST Integration", () => { status: 200, json: json ? async () => json : undefined, text: text ? async () => text : undefined, - headers: { get: key => key === "content-length" ? 100 : header, raw: () => ({ "content-type": header }) } + headers: { + get: key => (key === "content-length" ? 100 : header), + raw: () => ({ "content-type": header }), + }, } } it("should be able to parse JSON response", async () => { - const input = buildInput({a: 1}, null, "application/json") + const input = buildInput({ a: 1 }, null, "application/json") const output = await config.integration.parseResponse(input) - expect(output.data).toEqual({a: 1}) + expect(output.data).toEqual({ a: 1 }) expect(output.info.code).toEqual(200) expect(output.info.size).toEqual("100B") - expect(output.extra.raw).toEqual(JSON.stringify({a: 1})) + expect(output.extra.raw).toEqual(JSON.stringify({ a: 1 })) expect(output.extra.headers["content-type"]).toEqual("application/json") }) @@ -192,7 +199,7 @@ describe("REST Integration", () => { const text = "12" const input = buildInput(null, text, "application/xml") const output = await config.integration.parseResponse(input) - expect(output.data).toEqual({a: "1", b: "2"}) + expect(output.data).toEqual({ a: "1", b: "2" }) expect(output.extra.raw).toEqual(text) expect(output.extra.headers["content-type"]).toEqual("application/xml") }) @@ -202,53 +209,309 @@ describe("REST Integration", () => { const basicAuth = { _id: "c59c14bd1898a43baa08da68959b24686", name: "basic-1", - type : AuthType.BASIC, - config : { + type: AuthType.BASIC, + config: { username: "user", - password: "password" - } + password: "password", + }, } - + const bearerAuth = { _id: "0d91d732f34e4befabeff50b392a8ff3", name: "bearer-1", - type : AuthType.BEARER, - config : { - "token": "mytoken" - } + type: AuthType.BEARER, + config: { + token: "mytoken", + }, } beforeEach(() => { config = new TestConfiguration({ url: BASE_URL, - authConfigs : [basicAuth, bearerAuth] + authConfigs: [basicAuth, bearerAuth], }) }) it("adds basic auth", async () => { const query = { - authConfigId: "c59c14bd1898a43baa08da68959b24686" + authConfigId: "c59c14bd1898a43baa08da68959b24686", } await config.integration.read(query) expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, { method: "GET", headers: { - Authorization: "Basic dXNlcjpwYXNzd29yZA==" + Authorization: "Basic dXNlcjpwYXNzd29yZA==", }, }) }) it("adds bearer auth", async () => { const query = { - authConfigId: "0d91d732f34e4befabeff50b392a8ff3" + authConfigId: "0d91d732f34e4befabeff50b392a8ff3", } await config.integration.read(query) expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, { method: "GET", headers: { - Authorization: "Bearer mytoken" + Authorization: "Bearer mytoken", }, }) }) }) + + describe("page based pagination", () => { + it("can paginate using query params", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + path: "api", + pagination: { + type: "page", + location: "query", + pageParam, + sizeParam, + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + await config.integration.read(query) + expect(fetch).toHaveBeenCalledWith( + `${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}&`, + { + headers: {}, + method: "GET", + } + ) + }) + + it("can paginate using JSON request body", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + bodyType: "json", + path: "api", + pagination: { + type: "page", + location: "body", + pageParam, + sizeParam, + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + await config.integration.create(query) + expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, { + body: JSON.stringify({ + [pageParam]: pageValue, + [sizeParam]: sizeValue, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }) + }) + + it("can paginate using form-data request body", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + bodyType: "form", + path: "api", + pagination: { + type: "page", + location: "body", + pageParam, + sizeParam, + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + await config.integration.create(query) + expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, { + body: expect.any(FormData), + headers: {}, + method: "POST", + }) + const sentData = JSON.stringify(fetch.mock.calls[0][1].body) + expect(sentData).toContain(pageParam) + expect(sentData).toContain(sizeParam) + }) + + it("can paginate using form-encoded request body", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + bodyType: "encoded", + path: "api", + pagination: { + type: "page", + location: "body", + pageParam, + sizeParam, + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + await config.integration.create(query) + expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, { + body: expect.any(URLSearchParams), + headers: {}, + method: "POST", + }) + const sentData = fetch.mock.calls[0][1].body + expect(sentData.has(pageParam)) + expect(sentData.get(pageParam)).toEqual(pageValue.toString()) + expect(sentData.has(sizeParam)) + expect(sentData.get(sizeParam)).toEqual(sizeValue.toString()) + }) + }) + + describe("cursor based pagination", () => { + it("can paginate using query params", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + path: "api", + pagination: { + type: "cursor", + location: "query", + pageParam, + sizeParam, + responseParam: "my_next_cursor", + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + const res = await config.integration.read(query) + expect(fetch).toHaveBeenCalledWith( + `${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}&`, + { + headers: {}, + method: "GET", + } + ) + expect(res.pagination.cursor).toEqual(123) + }) + + it("can paginate using JSON request body", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + bodyType: "json", + path: "api", + pagination: { + type: "page", + location: "body", + pageParam, + sizeParam, + responseParam: "my_next_cursor", + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + const res = await config.integration.create(query) + expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, { + body: JSON.stringify({ + [pageParam]: pageValue, + [sizeParam]: sizeValue, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }) + expect(res.pagination.cursor).toEqual(123) + }) + + it("can paginate using form-data request body", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + bodyType: "form", + path: "api", + pagination: { + type: "page", + location: "body", + pageParam, + sizeParam, + responseParam: "my_next_cursor", + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + const res = await config.integration.create(query) + expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, { + body: expect.any(FormData), + headers: {}, + method: "POST", + }) + const sentData = JSON.stringify(fetch.mock.calls[0][1].body) + expect(sentData).toContain(pageParam) + expect(sentData).toContain(sizeParam) + expect(res.pagination.cursor).toEqual(123) + }) + + it("can paginate using form-encoded request body", async () => { + const pageParam = "my_page_param" + const sizeParam = "my_size_param" + const pageValue = 3 + const sizeValue = 10 + const query = { + bodyType: "encoded", + path: "api", + pagination: { + type: "page", + location: "body", + pageParam, + sizeParam, + responseParam: "my_next_cursor", + }, + paginationValues: { + page: pageValue, + limit: sizeValue, + }, + } + const res = await config.integration.create(query) + expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, { + body: expect.any(URLSearchParams), + headers: {}, + method: "POST", + }) + const sentData = fetch.mock.calls[0][1].body + expect(sentData.has(pageParam)) + expect(sentData.get(pageParam)).toEqual(pageValue.toString()) + expect(sentData.has(sizeParam)) + expect(sentData.get(sizeParam)).toEqual(sizeValue.toString()) + expect(res.pagination.cursor).toEqual(123) + }) + }) })