From a2165cf9021c03073f87af3e6873dfe8a53faa8b Mon Sep 17 00:00:00 2001 From: ekremney Date: Tue, 12 Dec 2023 17:36:01 +0100 Subject: [PATCH 1/2] feat: http-utils functions --- package-lock.json | 15 ++++-- packages/spacecat-shared-utils/README.md | 7 +++ packages/spacecat-shared-utils/package.json | 3 ++ .../spacecat-shared-utils/src/http-utils.js | 39 ++++++++++++++ packages/spacecat-shared-utils/src/index.d.ts | 31 +++++++++++ packages/spacecat-shared-utils/src/index.js | 7 +++ .../test/http-utils.test.js | 51 +++++++++++++++++++ .../spacecat-shared-utils/test/index.test.js | 4 ++ 8 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 packages/spacecat-shared-utils/src/http-utils.js create mode 100644 packages/spacecat-shared-utils/test/http-utils.test.js diff --git a/package-lock.json b/package-lock.json index 8af1962f1..4f7671e3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12371,7 +12371,7 @@ }, "packages/spacecat-shared-rum-api-client": { "name": "@adobe/spacecat-shared-rum-api-client", - "version": "1.1.0", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "4.1.1", @@ -12389,12 +12389,21 @@ "typescript": "5.3.2" } }, + "packages/spacecat-shared-rum-api-client/node_modules/@adobe/spacecat-shared-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-utils/-/spacecat-shared-utils-1.3.0.tgz", + "integrity": "sha512-w+A5ZUo80tzt0ni5baoq+PUMkRHlUtFhGt9RtGNwLuhCf2yYP2lhfBTCICn8fpx3lmGkfngXRjaIsgZJWiLO/w==" + }, "packages/spacecat-shared-utils": { "name": "@adobe/spacecat-shared-utils", - "version": "1.3.0", + "version": "1.4.0", "license": "Apache-2.0", + "dependencies": { + "@adobe/fetch": "^4.1.1" + }, "devDependencies": { - "chai": "4.3.10" + "chai": "4.3.10", + "sinon": "17.0.1" } } } diff --git a/packages/spacecat-shared-utils/README.md b/packages/spacecat-shared-utils/README.md index 8734f467d..3dfb186a2 100644 --- a/packages/spacecat-shared-utils/README.md +++ b/packages/spacecat-shared-utils/README.md @@ -45,6 +45,13 @@ The library includes the following utility functions: - `hasText(str)`: Checks if the given string is not empty. - `dateAfterDays(number)`: Calculates the date after a specified number of days from the current date. +The library includes the following http utility functions to create `Response` objects: + +- `ok(body)`: Creates a 200 response with a JSON body. +- `badRequest(message)`: Creates a 400 response with a JSON body. +- `notFound(message)`: Creates a 404 response with a JSON body. +- `internalServerError(message)`: Creates a 500 response with a JSON body. + ## Testing This library includes a comprehensive test suite to ensure the reliability of the utility functions. To run the tests, use the following command: diff --git a/packages/spacecat-shared-utils/package.json b/packages/spacecat-shared-utils/package.json index 1742cb3ff..614ae31d7 100644 --- a/packages/spacecat-shared-utils/package.json +++ b/packages/spacecat-shared-utils/package.json @@ -31,5 +31,8 @@ "devDependencies": { "chai": "4.3.10", "sinon": "17.0.1" + }, + "dependencies": { + "@adobe/fetch": "4.1.1" } } diff --git a/packages/spacecat-shared-utils/src/http-utils.js b/packages/spacecat-shared-utils/src/http-utils.js new file mode 100644 index 000000000..76c344d8f --- /dev/null +++ b/packages/spacecat-shared-utils/src/http-utils.js @@ -0,0 +1,39 @@ +/* + * Copyright 2023 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. + */ + +import { Response } from '@adobe/fetch'; + +function createResponse(body, status = 200) { + return new Response( + JSON.stringify(body), + { + headers: { 'content-type': 'application/json' }, + status, + }, + ); +} + +export function ok(body) { + return createResponse(body, 200); +} + +export function badRequest(message) { + return createResponse({ message }, 400); +} + +export function notFound(message) { + return createResponse({ message }, 404); +} + +export function internalServerError(message) { + return createResponse({ message }, 500); +} diff --git a/packages/spacecat-shared-utils/src/index.d.ts b/packages/spacecat-shared-utils/src/index.d.ts index 5f7a590be..26147e79c 100644 --- a/packages/spacecat-shared-utils/src/index.d.ts +++ b/packages/spacecat-shared-utils/src/index.d.ts @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ +/** UTILITY FUNCTIONS */ export function arrayEquals(a: T[], b: T[]): boolean; export function hasText(str: string): boolean; @@ -35,3 +36,33 @@ export function toBoolean(value: unknown): boolean; export function isValidUrl(urlString: string): boolean; export function dateAfterDays(days: string): Date; + +/** HTTP UTILS */ + +/** + * Creates a 200 response with a JSON body. + * @param {object} body - JSON body. + * @return {Response} Response. + */ +export function ok(body: object): Response; + +/** + * Creates a 400 response with a JSON body. + * @param {string} message - Error message. + * @return {Response} Response. + */ +export function badRequest(message: string): Response; + +/** + * Creates a 404 response with a JSON body. + * @param {string} message - Error message. + * @return {Response} Response. + */ +export function notFound(message: string): Response; + +/** + * Creates a 500 response with a JSON body. + * @param {string} message - Error message. + * @return {Response} Response. + */ +export function internalServerError(message: string): Response; diff --git a/packages/spacecat-shared-utils/src/index.js b/packages/spacecat-shared-utils/src/index.js index 1378686b3..7560c947a 100644 --- a/packages/spacecat-shared-utils/src/index.js +++ b/packages/spacecat-shared-utils/src/index.js @@ -27,4 +27,11 @@ export { dateAfterDays, } from './functions.js'; +export { + ok, + badRequest, + notFound, + internalServerError, +} from './http-utils.js'; + export { resolveSecretsName } from './helpers.js'; diff --git a/packages/spacecat-shared-utils/test/http-utils.test.js b/packages/spacecat-shared-utils/test/http-utils.test.js new file mode 100644 index 000000000..a0b18ad8e --- /dev/null +++ b/packages/spacecat-shared-utils/test/http-utils.test.js @@ -0,0 +1,51 @@ +/* + * Copyright 2023 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. + */ +/* eslint-env mocha */ +import { expect } from 'chai'; +import { + ok, badRequest, notFound, internalServerError, +} from '../src/http-utils.js'; + +describe('http-utils', () => { + it('ok should return a 200 response with JSON body', async () => { + const body = { key: 'value' }; + const response = ok(body); + expect(response.status).to.equal(200); + expect(response.headers.get('content-type')).to.equal('application/json'); + const respJson = await response.json(); + expect(respJson).to.eql(body); + }); + + it('badRequest should return a 400 response with JSON body', async () => { + const response = badRequest('Bad Request'); + expect(response.status).to.equal(400); + expect(response.headers.get('content-type')).to.equal('application/json'); + const respJson = await response.json(); + expect(respJson).to.eql({ message: 'Bad Request' }); + }); + + it('notFound return a 404 response with JSON body', async () => { + const response = notFound('Not Found'); + expect(response.status).to.equal(404); + expect(response.headers.get('content-type')).to.equal('application/json'); + const respJson = await response.json(); + expect(respJson).to.eql({ message: 'Not Found' }); + }); + + it('internalServerError should return a 500 response with JSON body', async () => { + const response = internalServerError('Internal Server Error'); + expect(response.status).to.equal(500); + expect(response.headers.get('content-type')).to.equal('application/json'); + const respJson = await response.json(); + expect(respJson).to.eql({ message: 'Internal Server Error' }); + }); +}); diff --git a/packages/spacecat-shared-utils/test/index.test.js b/packages/spacecat-shared-utils/test/index.test.js index e6333939d..2f532eca7 100644 --- a/packages/spacecat-shared-utils/test/index.test.js +++ b/packages/spacecat-shared-utils/test/index.test.js @@ -32,6 +32,10 @@ describe('Index Exports', () => { 'isValidUrl', 'resolveSecretsName', 'dateAfterDays', + 'ok', + 'badRequest', + 'notFound', + 'internalServerError', ]; it('exports all expected functions', () => { From de4cfe6a692c4cbd0f8284cc5eb24316c690884b Mon Sep 17 00:00:00 2001 From: ekremney Date: Thu, 14 Dec 2023 10:05:41 +0100 Subject: [PATCH 2/2] feat: http-utils functions --- .../spacecat-shared-utils/src/http-utils.js | 20 +++++++++++++++---- packages/spacecat-shared-utils/src/index.js | 1 + .../test/http-utils.test.js | 13 ++++++------ .../spacecat-shared-utils/test/index.test.js | 1 + 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/spacecat-shared-utils/src/http-utils.js b/packages/spacecat-shared-utils/src/http-utils.js index 76c344d8f..5a0dc25aa 100644 --- a/packages/spacecat-shared-utils/src/http-utils.js +++ b/packages/spacecat-shared-utils/src/http-utils.js @@ -12,20 +12,30 @@ import { Response } from '@adobe/fetch'; -function createResponse(body, status = 200) { +/** + * Creates a response with a JSON body. Defaults to 200 status. + * @param {object} body - JSON body. + * @param {number} status - Optional status code. + * @param {object} headers - Optional headers. + * @return {Response} Response. + */ +export function createResponse(body, status = 200, headers = {}) { return new Response( JSON.stringify(body), { - headers: { 'content-type': 'application/json' }, + headers: { 'content-type': 'application/json; charset=utf-8', ...headers }, status, }, ); } - export function ok(body) { return createResponse(body, 200); } +export function noContent(body) { + return createResponse(body, 204); +} + export function badRequest(message) { return createResponse({ message }, 400); } @@ -35,5 +45,7 @@ export function notFound(message) { } export function internalServerError(message) { - return createResponse({ message }, 500); + return createResponse({ message }, 500, { + 'x-error': `internal server error: ${message}`, + }); } diff --git a/packages/spacecat-shared-utils/src/index.js b/packages/spacecat-shared-utils/src/index.js index 7560c947a..a7c8036a2 100644 --- a/packages/spacecat-shared-utils/src/index.js +++ b/packages/spacecat-shared-utils/src/index.js @@ -29,6 +29,7 @@ export { export { ok, + noContent, badRequest, notFound, internalServerError, diff --git a/packages/spacecat-shared-utils/test/http-utils.test.js b/packages/spacecat-shared-utils/test/http-utils.test.js index a0b18ad8e..3450bb986 100644 --- a/packages/spacecat-shared-utils/test/http-utils.test.js +++ b/packages/spacecat-shared-utils/test/http-utils.test.js @@ -20,7 +20,7 @@ describe('http-utils', () => { const body = { key: 'value' }; const response = ok(body); expect(response.status).to.equal(200); - expect(response.headers.get('content-type')).to.equal('application/json'); + expect(response.headers.get('content-type')).to.equal('application/json; charset=utf-8'); const respJson = await response.json(); expect(respJson).to.eql(body); }); @@ -28,7 +28,7 @@ describe('http-utils', () => { it('badRequest should return a 400 response with JSON body', async () => { const response = badRequest('Bad Request'); expect(response.status).to.equal(400); - expect(response.headers.get('content-type')).to.equal('application/json'); + expect(response.headers.get('content-type')).to.equal('application/json; charset=utf-8'); const respJson = await response.json(); expect(respJson).to.eql({ message: 'Bad Request' }); }); @@ -36,16 +36,17 @@ describe('http-utils', () => { it('notFound return a 404 response with JSON body', async () => { const response = notFound('Not Found'); expect(response.status).to.equal(404); - expect(response.headers.get('content-type')).to.equal('application/json'); + expect(response.headers.get('content-type')).to.equal('application/json; charset=utf-8'); const respJson = await response.json(); expect(respJson).to.eql({ message: 'Not Found' }); }); it('internalServerError should return a 500 response with JSON body', async () => { - const response = internalServerError('Internal Server Error'); + const response = internalServerError('uh oh'); expect(response.status).to.equal(500); - expect(response.headers.get('content-type')).to.equal('application/json'); + expect(response.headers.get('content-type')).to.equal('application/json; charset=utf-8'); + expect(response.headers.get('x-error')).to.equal('internal server error: uh oh'); const respJson = await response.json(); - expect(respJson).to.eql({ message: 'Internal Server Error' }); + expect(respJson).to.eql({ message: 'uh oh' }); }); }); diff --git a/packages/spacecat-shared-utils/test/index.test.js b/packages/spacecat-shared-utils/test/index.test.js index 2f532eca7..3f7bc8da2 100644 --- a/packages/spacecat-shared-utils/test/index.test.js +++ b/packages/spacecat-shared-utils/test/index.test.js @@ -33,6 +33,7 @@ describe('Index Exports', () => { 'resolveSecretsName', 'dateAfterDays', 'ok', + 'noContent', 'badRequest', 'notFound', 'internalServerError',