diff --git a/packages/azure-functions/package.json b/packages/azure-functions/package.json index 95b807d1..6942f8ef 100644 --- a/packages/azure-functions/package.json +++ b/packages/azure-functions/package.json @@ -20,8 +20,8 @@ "author": "Thada Wangthammang", "license": "MIT", "dependencies": { - "@nammatham/core": "2.0.0-alpha.8", "@azure/functions": "^4.1.0", + "@nammatham/core": "2.0.0-alpha.8", "colorette": "^2.0.20", "express": "^4.18.2", "undici": "5.20.0", @@ -36,6 +36,7 @@ }, "devDependencies": { "@types/express": "^4.17.21", - "@types/uuid": "^9.0.7" + "@types/uuid": "^9.0.7", + "node-mocks-http": "^1.14.1" } } \ No newline at end of file diff --git a/packages/azure-functions/src/http/HttpRequest.test.ts b/packages/azure-functions/src/http/HttpRequest.test.ts new file mode 100644 index 00000000..7c75a51c --- /dev/null +++ b/packages/azure-functions/src/http/HttpRequest.test.ts @@ -0,0 +1,37 @@ +import { expect, test } from 'vitest'; +import { HttpRequest } from './HttpRequest'; +import httpMocks from 'node-mocks-http'; +import exp from 'constants'; + +test('Test HttpRequest', () => { + // Arrange + const req = httpMocks.createRequest({ + method: 'GET', + protocol: 'https', + // get: (key: string) => 'localhost', + url: '/api/test', + query: { + a: '1', + b: '2', + }, + headers: { + x: '1', + y: '2', + }, + }); + + // Act + const result = new HttpRequest(req); + + // Assert + expect(result.method).toBe('GET'); + expect(result.url).toBe('https://undefined/api/test'); + expect(result.query.get('a')).toBe('1'); + expect(result.query.get('b')).toBe('2'); + expect(result.headers.get('x')).toBe('1'); + expect(result.headers.get('y')).toBe('2'); + expect(result.body).toBe(null); + expect(result.params).toStrictEqual({}); + expect(result.user).toBe(null); + expect(result.bodyUsed).toBe(false); +}); diff --git a/packages/azure-functions/src/http/HttpRequest.ts b/packages/azure-functions/src/http/HttpRequest.ts index f628b446..bdc8fbe3 100644 --- a/packages/azure-functions/src/http/HttpRequest.ts +++ b/packages/azure-functions/src/http/HttpRequest.ts @@ -3,11 +3,11 @@ import type * as types from '@azure/functions'; import type { HttpRequestParams, HttpRequestUser } from '@azure/functions'; import type { Blob } from 'buffer'; import type express from 'express'; -import { logger } from '@nammatham/core'; +import type { URLSearchParams } from 'url'; import type { ReadableStream } from 'stream/web'; import type { FormData, Headers } from 'undici'; +import { logger } from '@nammatham/core'; import { Request as uRequest } from 'undici'; -import type { URLSearchParams } from 'url'; import { convertExpressQueryToURLSearchParams, convertExpressReqHeaderToHeadersInit, @@ -25,7 +25,7 @@ export class HttpRequest implements types.HttpRequest { const url = getExpressReqFullUrl(req); this.#body = req.body; this.#uReq = new uRequest(url, { - body: this.#body, + body: req.method === 'GET' ? undefined : req.body, method: req.method, headers: convertExpressReqHeaderToHeadersInit(req.headers), }); diff --git a/packages/azure-functions/src/http/http-helper.test.ts b/packages/azure-functions/src/http/http-helper.test.ts new file mode 100644 index 00000000..7f3b6d7e --- /dev/null +++ b/packages/azure-functions/src/http/http-helper.test.ts @@ -0,0 +1,83 @@ +import { expect, test } from 'vitest'; +import * as httpHelpers from './http-helpers'; +import e, { Request as ExpressRequest } from 'express'; + +test('Test convertExpressQueryToURLSearchParams with string', () => { + // Arrange + const query = { + a: '1', + b: '2', + c: '3', + }; + + // Act + const result = httpHelpers.convertExpressQueryToURLSearchParams(query); + + // Assert + expect(result).toBeInstanceOf(URLSearchParams); + expect(result.get('a')).toBe('1'); + expect(result.get('b')).toBe('2'); + expect(result.get('c')).toBe('3'); + expect(result.toString()).toBe('a=1&b=2&c=3'); +}); + +test('Test convertExpressQueryToURLSearchParams with array', () => { + // Arrange + const query = { + a: ['1', '2', '3'], + b: ['4', '5', '6'], + }; + + // Act + const result = httpHelpers.convertExpressQueryToURLSearchParams(query); + + // Assert + expect(result).toBeInstanceOf(URLSearchParams); + expect(result.get('a')).toBe('1'); + expect(result.get('b')).toBe('4'); + expect(result.toString()).toBe('a=1&a=2&a=3&b=4&b=5&b=6'); +}); + +test('Test convertExpressHeadersToHeaders with string', () => { + // Arrange + const headers = { + a: '1', + b: '2', + c: '3', + }; + + // Act + const result = httpHelpers.convertExpressReqHeaderToHeadersInit(headers); + + // Assert + expect(result).toStrictEqual(headers); +}); + +test('Test convertExpressHeadersToHeaders with array', () => { + // Arrange + const headers = { + a: ['1', '2', '3'], + b: ['4', '5', '6'], + }; + + // Act + const result = httpHelpers.convertExpressReqHeaderToHeadersInit(headers); + + // Assert + expect(result).toStrictEqual(headers); +}); + +test('Test getExpressReqFullUrl', () => { + // Arrange + const req = { + protocol: 'https', + get: () => 'localhost', + originalUrl: '/api/test', + } as any as ExpressRequest; + + // Act + const result = httpHelpers.getExpressReqFullUrl(req); + + // Assert + expect(result).toBe('https://localhost/api/test'); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d3409f9..78d7282a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -211,6 +211,9 @@ importers: '@types/uuid': specifier: ^9.0.7 version: 9.0.7 + node-mocks-http: + specifier: ^1.14.1 + version: 1.14.1 packages/core: dependencies: @@ -1360,7 +1363,6 @@ packages: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: false /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1768,7 +1770,6 @@ packages: engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.2.1 - dev: false /content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} @@ -1893,6 +1894,11 @@ packages: engines: {node: '>=0.4.0'} dev: true + /depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + dev: true + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2450,7 +2456,6 @@ packages: /fresh@0.5.2: resolution: {integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=} engines: {node: '>= 0.6'} - dev: false /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -3120,7 +3125,6 @@ packages: /media-typer@0.3.0: resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} engines: {node: '>= 0.6'} - dev: false /memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} @@ -3129,7 +3133,6 @@ packages: /merge-descriptors@1.0.1: resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=} - dev: false /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -3143,7 +3146,6 @@ packages: /methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - dev: false /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -3167,7 +3169,6 @@ packages: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} hasBin: true - dev: false /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -3256,7 +3257,6 @@ packages: /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - dev: false /nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} @@ -3271,6 +3271,24 @@ packages: hasBin: true dev: true + /node-mocks-http@1.14.1: + resolution: {integrity: sha512-mfXuCGonz0A7uG1FEjnypjm34xegeN5+HI6xeGhYKecfgaZhjsmYoLE9LEFmT+53G1n8IuagPZmVnEL/xNsFaA==} + engines: {node: '>=14'} + dependencies: + '@types/express': 4.17.21 + '@types/node': 20.10.6 + accepts: 1.3.8 + content-disposition: 0.5.4 + depd: 1.1.2 + fresh: 0.5.2 + merge-descriptors: 1.0.1 + methods: 1.1.2 + mime: 1.6.0 + parseurl: 1.3.3 + range-parser: 1.2.1 + type-is: 1.6.18 + dev: true + /nodemon@2.0.22: resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} engines: {node: '>=8.10.0'} @@ -3520,7 +3538,6 @@ packages: /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - dev: false /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -3764,7 +3781,6 @@ packages: /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - dev: false /raw-body@2.5.1: resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} @@ -4513,7 +4529,6 @@ packages: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: false /typed-array-buffer@1.0.0: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}