Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/jin-frame/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
33 changes: 33 additions & 0 deletions packages/jin-frame/src/frames/AbstractJinFrame.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
15 changes: 10 additions & 5 deletions packages/jin-frame/src/frames/AbstractJinFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,13 @@ export abstract class AbstractJinFrame {
return safeStringify(data);
}

public _getBaseUrlString(paths: Record<string, string>): string {
const host = getUrlValue(this.#option.host);
const pathPrefix = getUrlValue(this.#option.pathPrefix);
const path = getUrlValue(this.#option.path);
public _getBaseUrlString(
paths: Record<string, string>,
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;
Expand Down Expand Up @@ -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;
Expand Down
17 changes: 17 additions & 0 deletions packages/jin-frame/src/interfaces/options/JinFrameRequestConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading