diff --git a/src/FunctionsClient.ts b/src/FunctionsClient.ts index 79968b5..6e044db 100644 --- a/src/FunctionsClient.ts +++ b/src/FunctionsClient.ts @@ -6,11 +6,13 @@ import { FunctionsRelayError, FunctionsResponse, FunctionInvokeOptions, + FunctionRegion, } from './types' export class FunctionsClient { protected url: string protected headers: Record + protected region: FunctionRegion protected fetch: Fetch constructor( @@ -18,13 +20,16 @@ export class FunctionsClient { { headers = {}, customFetch, + region = FunctionRegion.Any, }: { headers?: Record customFetch?: Fetch + region?: FunctionRegion } = {} ) { this.url = url this.headers = headers + this.region = region this.fetch = resolveFetch(customFetch) } @@ -47,8 +52,14 @@ export class FunctionsClient { ): Promise> { try { const { headers, method, body: functionArgs } = options - let _headers: Record = {} + let { region } = options + if (!region) { + region = this.region + } + if (region && region !== 'any') { + _headers['x-region'] = region + } let body: any if ( functionArgs && diff --git a/src/types.ts b/src/types.ts index 9e0646b..3fca51c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -40,6 +40,24 @@ export class FunctionsHttpError extends FunctionsError { super('Edge Function returned a non-2xx status code', 'FunctionsHttpError', context) } } +// Define the enum for the 'region' property +export enum FunctionRegion { + Any = 'any', + ApNortheast1 = 'ap-northeast-1', + ApNortheast2 = 'ap-northeast-2', + ApSouth1 = 'ap-south-1', + ApSoutheast1 = 'ap-southeast-1', + ApSoutheast2 = 'ap-southeast-2', + CaCentral1 = 'ca-central-1', + EuCentral1 = 'eu-central-1', + EuWest1 = 'eu-west-1', + EuWest2 = 'eu-west-2', + EuWest3 = 'eu-west-3', + SaEast1 = 'sa-east-1', + UsEast1 = 'us-east-1', + UsWest1 = 'us-west-1', + UsWest2 = 'us-west-2', +} export type FunctionInvokeOptions = { /** @@ -50,6 +68,10 @@ export type FunctionInvokeOptions = { * The HTTP verb of the request */ method?: 'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE' + /** + * The Region to invoke the function in. + */ + region?: FunctionRegion; /** * The body of the request. */ diff --git a/test/spec/params.spec.ts b/test/spec/params.spec.ts index ee8c9be..63c1786 100644 --- a/test/spec/params.spec.ts +++ b/test/spec/params.spec.ts @@ -7,7 +7,7 @@ import { sign } from 'jsonwebtoken' import { ContentType } from 'allure-js-commons' import { FunctionsClient } from '../../src/index' - +import { FunctionRegion } from '../../src/types' import { Relay, runRelay } from '../relay/container' import { attach, log } from '../utils/jest-custom-reporter' import { str2ab } from '../utils/binaries' @@ -146,6 +146,158 @@ describe('params reached to function', () => { ).toBe(true) }) + test('invoke mirror set valid region on request', async () => { + /** + * @feature headers + */ + log('create FunctionsClient') + const fclient = new FunctionsClient(`http://localhost:${relay.container.getMappedPort(8081)}`) + + log('invoke mirror') + const customHeader = nanoid() + const validRegion = FunctionRegion.ApNortheast1 + + const { data, error } = await fclient.invoke('mirror', { + headers: { + 'custom-header': customHeader, + Authorization: `Bearer ${apiKey}`, + }, + region: validRegion, + }) + + log('assert no error') + const expected = { + url: 'http://localhost:8000/mirror', + method: 'POST', + headers: data?.headers ?? [], + body: '', + } + expect(data).toEqual(expected) + attach( + 'check headers from function', + `expected to include: ${['custom-header', customHeader]}\n actual: ${JSON.stringify( + data?.headers + )}`, + ContentType.TEXT + ) + console.log(data?.headers) + expect( + (data?.headers as [Array]).filter(([k, v]) => k === 'x-region' && v === validRegion) + .length > 0 + ).toBe(true) + }) + + test('invoke with region overrides region in the client', async () => { + /** + * @feature headers + */ + log('create FunctionsClient') + const fclient = new FunctionsClient(`http://localhost:${relay.container.getMappedPort(8081)}`, { + region: FunctionRegion.ApNortheast1, + }) + + log('invoke mirror') + const customHeader = nanoid() + const validRegion = FunctionRegion.ApSoutheast1 + + const { data, error } = await fclient.invoke('mirror', { + headers: { + 'custom-header': customHeader, + Authorization: `Bearer ${apiKey}`, + }, + region: validRegion, + }) + + log('assert no error') + const expected = { + url: 'http://localhost:8000/mirror', + method: 'POST', + headers: data?.headers ?? [], + body: '', + } + expect(data).toEqual(expected) + attach( + 'check headers from function', + `expected to include: ${['custom-header', customHeader]}\n actual: ${JSON.stringify( + data?.headers + )}`, + ContentType.TEXT + ) + console.log(data?.headers) + expect( + (data?.headers as [Array]).filter(([k, v]) => k === 'x-region' && v === validRegion) + .length > 0 + ).toBe(true) + }) + + test('starts client with default region, invoke reverts to any (no x-region header)', async () => { + /** + * @feature headers + */ + log('create FunctionsClient') + const validRegion = FunctionRegion.ApSoutheast1 + const fclient = new FunctionsClient(`http://localhost:${relay.container.getMappedPort(8081)}`, { + region: validRegion, + }) + + log('invoke mirror') + const customHeader = nanoid() + + const { data, error } = await fclient.invoke('mirror', { + headers: { + 'custom-header': customHeader, + Authorization: `Bearer ${apiKey}`, + }, + region: FunctionRegion.Any + }) + + log('assert no error') + const expected = { + url: 'http://localhost:8000/mirror', + method: 'POST', + headers: data?.headers ?? [], + body: '', + } + expect(data).toEqual(expected) + attach( + 'check headers from function', + `expected to include: ${['custom-header', customHeader]}\n actual: ${JSON.stringify( + data?.headers + )}`, + ContentType.TEXT + ) + console.log(data?.headers) + expect( + (data?.headers as [Array]).filter(([k, v]) => k === 'x-region' && v === validRegion) + .length == 0 + ).toBe(true) + }) + + test('invoke region set only on the constructor', async () => { + /** + * @feature headers + */ + log('create FunctionsClient') + const fclient = new FunctionsClient(`http://localhost:${relay.container.getMappedPort(8081)}`,{region: FunctionRegion.ApNortheast1}) + + log('invoke mirror') + const customHeader = nanoid() + + + const { data, error } = await fclient.invoke('mirror', { + headers: { + 'custom-header': customHeader, + Authorization: `Bearer ${apiKey}` + }, + }) + + log('assert no error') + expect( + (data?.headers as [Array]).filter(([k, v]) => k === 'x-region' && v === FunctionRegion.ApNortheast1) + .length > 0 + ).toBe(true) + }) + test('invoke mirror with body formData', async () => { /** * @feature body