diff --git a/packages/jin-frame/package.json b/packages/jin-frame/package.json index 4f044c2..def07e9 100644 --- a/packages/jin-frame/package.json +++ b/packages/jin-frame/package.json @@ -1,6 +1,6 @@ { "name": "jin-frame", - "version": "5.0.4", + "version": "5.1.0", "description": "Reusable HTTP API request definition library", "packageManager": "pnpm@10.16.0", "scripts": { diff --git a/packages/jin-frame/src/frames/AbstractJinFrame.test.ts b/packages/jin-frame/src/frames/AbstractJinFrame.test.ts index 3523ce7..f3048db 100644 --- a/packages/jin-frame/src/frames/AbstractJinFrame.test.ts +++ b/packages/jin-frame/src/frames/AbstractJinFrame.test.ts @@ -189,3 +189,36 @@ describe('AbstractJinFrame', () => { expect(frame._getData('param')).toMatchObject({ passing: 'pass,fail' }); }); }); + +describe('_request with runtime host/pathPrefix/path override', () => { + it('should override host at runtime', () => { + const frame = Test001PostFrame.of({ username: 'ironman', password: 'avengers', passing: 'pass' }); + const req = frame._request({ host: 'http://staging.api.google.com' }); + expect(req.url).toContain('staging.api.google.com'); + expect(req.url).not.toContain('some.api.google.com'); + }); + + it('should override path at runtime while keeping decorator pathPrefix', () => { + const frame = Test001PostFrame.of({ username: 'ironman', password: 'avengers', passing: 'pass' }); + const req = frame._request({ path: '/override/{passing}' }); + expect(req.url).toContain('/override/pass'); + expect(req.url).toContain('/jinframe/'); + }); + + it('should override pathPrefix at runtime', () => { + const frame = Test001PostFrame.of({ username: 'ironman', password: 'avengers', passing: 'pass' }); + const req = frame._request({ pathPrefix: '/v2' }); + expect(req.url).toContain('/v2/'); + expect(req.url).not.toContain('/jinframe/'); + }); + + it('should override all three at runtime', () => { + const frame = Test001PostFrame.of({ username: 'ironman', password: 'avengers', passing: 'pass' }); + const req = frame._request({ + host: 'http://staging.api.google.com', + pathPrefix: '/v2', + path: '/override/{passing}', + }); + expect(req.url).toBe('http://staging.api.google.com/v2/override/pass'); + }); +}); diff --git a/packages/jin-frame/src/frames/AbstractJinFrame.ts b/packages/jin-frame/src/frames/AbstractJinFrame.ts index 8be5581..68e0662 100644 --- a/packages/jin-frame/src/frames/AbstractJinFrame.ts +++ b/packages/jin-frame/src/frames/AbstractJinFrame.ts @@ -254,10 +254,13 @@ export abstract class AbstractJinFrame { return safeStringify(data); } - public _getBaseUrlString(paths: Record): string { - const host = getUrlValue(this.#option.host); - const pathPrefix = getUrlValue(this.#option.pathPrefix); - const path = getUrlValue(this.#option.path); + public _getBaseUrlString( + paths: Record, + override?: { host?: string; pathPrefix?: string; path?: string }, + ): string { + const host = override?.host ?? getUrlValue(this.#option.host); + const pathPrefix = override?.pathPrefix ?? getUrlValue(this.#option.pathPrefix); + const path = override?.path ?? getUrlValue(this.#option.path); // Expand URI templates before creating URL object to avoid encoding const expandedHost = host ? parseTemplate(host).expand(paths) : host; @@ -309,7 +312,9 @@ export abstract class AbstractJinFrame { this.#data.param = paths; // stage 05. url endpoint build and path parameter evaluation - const baseUrlString = option?.url ?? this._getBaseUrlString(paths); + const baseUrlString = + option?.url ?? + this._getBaseUrlString(paths, { host: option?.host, pathPrefix: option?.pathPrefix, path: option?.path }); // Expand URI template for option.url case const expandedUrlString = option?.url != null ? parseTemplate(baseUrlString).expand(paths) : baseUrlString; diff --git a/packages/jin-frame/src/interfaces/options/JinFrameRequestConfig.ts b/packages/jin-frame/src/interfaces/options/JinFrameRequestConfig.ts index af7f545..7c2828a 100644 --- a/packages/jin-frame/src/interfaces/options/JinFrameRequestConfig.ts +++ b/packages/jin-frame/src/interfaces/options/JinFrameRequestConfig.ts @@ -14,6 +14,23 @@ export interface JinFrameRequestConfig { url?: string; + /** + * Overrides the host defined in the frame decorator for this request only. + * Supports URI template syntax (e.g. `https://{tenant}.api.example.com`). + */ + host?: string; + + /** + * Overrides the pathPrefix defined in the frame decorator for this request only. + */ + pathPrefix?: string; + + /** + * Overrides the path defined in the frame decorator for this request only. + * Supports URI template syntax (e.g. `/users/{id}`). + */ + path?: string; + customBody?: unknown; auth?: JinBasicAuth;