Skip to content

Commit fc3ed43

Browse files
Merge pull request #13 from aystream/fix/api-base-url-priority
fix: profile apiBaseUrl takes priority over spec root server URL
2 parents cfbc395 + caa947a commit fc3ed43

3 files changed

Lines changed: 84 additions & 8 deletions

File tree

src/cli.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,11 @@ function buildRequestUrl(profile: Profile, command: CliCommand, flags: Record<st
247247
}
248248
});
249249

250-
const baseUrl = (command.serverUrl ?? profile.apiBaseUrl).replace(/\/+$/, "");
250+
const baseUrl = (
251+
command.serverUrlOverridesProfile
252+
? command.serverUrl ?? ""
253+
: profile.apiBaseUrl || command.serverUrl || ""
254+
).replace(/\/+$/, "");
251255
let url = baseUrl ? `${baseUrl}${pathValue}` : pathValue;
252256

253257
const queryParts: string[] = [];

src/openapi-to-commands.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface CliCommand {
2525
description?: string;
2626
requestContentType?: string;
2727
serverUrl?: string;
28+
serverUrlOverridesProfile?: boolean;
2829
}
2930

3031
type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "head" | "options" | "trace";
@@ -185,7 +186,7 @@ export class OpenapiToCommands {
185186
const name = multipleMethods ? `${baseName}_${op.method}` : baseName;
186187
const { options, requestContentType } = this.extractOptions(op, spec);
187188
const description = op.operation.summary ?? op.operation.description;
188-
const serverUrl = this.resolveOperationServerUrl(spec, op);
189+
const resolved = this.resolveOperationServerUrl(spec, op);
189190

190191
commands.push({
191192
name,
@@ -194,7 +195,8 @@ export class OpenapiToCommands {
194195
options,
195196
description,
196197
requestContentType,
197-
serverUrl,
198+
serverUrl: resolved.url,
199+
serverUrlOverridesProfile: resolved.overridesProfile,
198200
});
199201
}
200202
}
@@ -540,23 +542,28 @@ export class OpenapiToCommands {
540542
};
541543
}
542544

543-
private resolveOperationServerUrl(spec: OpenapiSpecLike, op: PathOperation): string | undefined {
545+
private resolveOperationServerUrl(
546+
spec: OpenapiSpecLike,
547+
op: PathOperation
548+
): { url?: string; overridesProfile?: boolean } {
544549
const rootBase = this.resolveServers(Array.isArray(spec?.servers) ? spec.servers : undefined);
550+
545551
const operationServer = this.resolveServers(op.operation.servers, rootBase);
546552
if (operationServer) {
547-
return operationServer;
553+
return { url: operationServer, overridesProfile: true };
548554
}
549555

550556
const pathServer = this.resolveServers(op.pathServers, rootBase);
551557
if (pathServer) {
552-
return pathServer;
558+
return { url: pathServer, overridesProfile: true };
553559
}
554560

555561
if (rootBase) {
556-
return rootBase;
562+
return { url: rootBase, overridesProfile: false };
557563
}
558564

559-
return this.resolveSwagger2BaseUrl(spec);
565+
const swagger2Base = this.resolveSwagger2BaseUrl(spec);
566+
return swagger2Base ? { url: swagger2Base, overridesProfile: false } : {};
560567
}
561568

562569
private resolveServers(rawServers?: unknown[], relativeTo?: string): string | undefined {

tests/cli.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,4 +1373,69 @@ describe("cli", () => {
13731373
expect(profile?.customHeaders).toEqual({ "X-Custom-Id": "abc123", "X-Tenant": "myorg" });
13741374
expect(profile?.commandPrefix).toBe("");
13751375
});
1376+
1377+
it.each([
1378+
{
1379+
label: "swagger 2.0 host",
1380+
spec: { swagger: "2.0", host: "gitlab.com", basePath: "/api/v4", schemes: ["https"], paths: { "/version": { get: { summary: "v" } } } },
1381+
cmd: "version",
1382+
},
1383+
{
1384+
label: "openapi 3.0 root servers",
1385+
spec: { openapi: "3.0.0", servers: [{ url: "https://public.example.com/api" }], paths: { "/status": { get: { summary: "s" } } } },
1386+
cmd: "status",
1387+
},
1388+
])("profile apiBaseUrl takes priority over $label", async ({ spec, cmd }) => {
1389+
const localDir = `${cwd}/.ocli`;
1390+
const cachePath = `${localDir}/specs/p.json`;
1391+
const capturedConfigs: unknown[] = [];
1392+
const fakeHttpClient: HttpClient = {
1393+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1394+
request: async (config: any) => {
1395+
capturedConfigs.push(config);
1396+
return { status: 200, statusText: "OK", headers: {}, config, data: {} };
1397+
},
1398+
};
1399+
const { profileStore, openapiLoader } = createCliDeps(cwd, homeDir, {
1400+
[`${localDir}/profiles.ini`]: ["[p]", "api_base_url = https://self-hosted.example.com", `openapi_spec_cache = ${cachePath}`, ""].join("\n"),
1401+
[cachePath]: JSON.stringify(spec),
1402+
[`${localDir}/current`]: "p",
1403+
});
1404+
1405+
await run([cmd], { cwd, profileStore, openapiLoader, httpClient: fakeHttpClient, stdout: () => {} });
1406+
1407+
const config = capturedConfigs[0] as { url: string };
1408+
expect(config.url).toContain("self-hosted.example.com");
1409+
expect(config.url).not.toContain("gitlab.com");
1410+
expect(config.url).not.toContain("public.example.com");
1411+
});
1412+
1413+
it("path-level servers override profile apiBaseUrl", async () => {
1414+
const localDir = `${cwd}/.ocli`;
1415+
const cachePath = `${localDir}/specs/ps.json`;
1416+
const spec = {
1417+
openapi: "3.0.0",
1418+
servers: [{ url: "https://root.example.com" }],
1419+
paths: { "/data": { servers: [{ url: "https://path-override.example.com" }], get: { summary: "d" } } },
1420+
};
1421+
1422+
const capturedConfigs: unknown[] = [];
1423+
const fakeHttpClient: HttpClient = {
1424+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1425+
request: async (config: any) => {
1426+
capturedConfigs.push(config);
1427+
return { status: 200, statusText: "OK", headers: {}, config, data: {} };
1428+
},
1429+
};
1430+
const { profileStore, openapiLoader } = createCliDeps(cwd, homeDir, {
1431+
[`${localDir}/profiles.ini`]: ["[ps]", "api_base_url = https://profile.example.com", `openapi_spec_cache = ${cachePath}`, ""].join("\n"),
1432+
[cachePath]: JSON.stringify(spec),
1433+
[`${localDir}/current`]: "ps",
1434+
});
1435+
1436+
await run(["data"], { cwd, profileStore, openapiLoader, httpClient: fakeHttpClient, stdout: () => {} });
1437+
1438+
const config = capturedConfigs[0] as { url: string };
1439+
expect(config.url).toBe("https://path-override.example.com/data");
1440+
});
13761441
});

0 commit comments

Comments
 (0)