Skip to content

Commit a486d2c

Browse files
committed
Merge branch 'pr/116'
2 parents 007fcd2 + 6ce148a commit a486d2c

5 files changed

Lines changed: 78 additions & 6 deletions

File tree

‎core/specs/polymarket/PolymarketClobAPI.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,7 @@ paths:
994994
# ----------------------------------------------------------------------------
995995
/geoblock:
996996
get:
997+
operationId: getGeoblock
997998
summary: Check Geoblock Status
998999
description: Check the geographic eligibility of the requesting IP address.
9991000
tags: [System]

‎core/src/BaseExchange.ts‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ export interface ApiEndpoint {
4545
isPrivate?: boolean;
4646
/** Identifier used to generate the implicit API method name. */
4747
operationId?: string;
48+
/**
49+
* When set, requests use this base URL instead of the descriptor default
50+
* (OpenAPI path- or operation-level `servers` override).
51+
*/
52+
baseUrl?: string;
4853
}
4954

5055
export interface ApiDescriptor {
@@ -1481,7 +1486,11 @@ export abstract class PredictionMarketExchange {
14811486
if (name in this) {
14821487
continue;
14831488
}
1484-
(this as any)[name] = this.createImplicitMethod(name, endpoint, descriptor.baseUrl);
1489+
(this as any)[name] = this.createImplicitMethod(
1490+
name,
1491+
endpoint,
1492+
endpoint.baseUrl ?? descriptor.baseUrl
1493+
);
14851494
}
14861495
}
14871496

@@ -1507,7 +1516,7 @@ export abstract class PredictionMarketExchange {
15071516
private createImplicitMethod(
15081517
name: string,
15091518
endpoint: ApiEndpoint,
1510-
baseUrl: string
1519+
resolvedBaseUrl: string
15111520
): (params?: Record<string, any>) => Promise<any> {
15121521
return async (params?: Record<string, any>): Promise<any> => {
15131522
const allParams = { ...(params || {}) };
@@ -1531,7 +1540,7 @@ export abstract class PredictionMarketExchange {
15311540
headers = this.sign(endpoint.method, resolvedPath, allParams);
15321541
}
15331542

1534-
const url = `${baseUrl}${resolvedPath}`;
1543+
const url = `${resolvedBaseUrl}${resolvedPath}`;
15351544
const method = endpoint.method.toUpperCase();
15361545

15371546
try {

‎core/src/exchanges/polymarket/api-clob.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ export const polymarketClobSpec = {
517517
},
518518
"/geoblock": {
519519
"get": {
520+
"operationId": "getGeoblock",
520521
"summary": "Check Geoblock Status",
521522
"tags": [
522523
"System"

‎core/src/utils/openapi.ts‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,27 @@ export function parseOpenApiSpec(spec: any, baseUrl?: string): ApiDescriptor {
4242
const topLevelSecurity = !!(spec.security && spec.security.length > 0);
4343

4444
const paths = spec.paths || {};
45-
for (const [path, methods] of Object.entries<any>(paths)) {
46-
for (const [httpMethod, operation] of Object.entries<any>(methods)) {
47-
// Skip non-HTTP-method keys like "parameters"
45+
for (const [path, pathItem] of Object.entries<any>(paths)) {
46+
if (!pathItem || typeof pathItem !== 'object') {
47+
continue;
48+
}
49+
const pathServerUrl = Array.isArray(pathItem.servers) && pathItem.servers[0]?.url
50+
? String(pathItem.servers[0].url).replace(/\/$/, '')
51+
: undefined;
52+
53+
for (const [httpMethod, operation] of Object.entries<any>(pathItem)) {
54+
// Skip non-HTTP-method keys like "parameters", "servers"
4855
if (!['get', 'post', 'put', 'patch', 'delete'].includes(httpMethod.toLowerCase())) {
4956
continue;
5057
}
58+
if (!operation || typeof operation !== 'object') {
59+
continue;
60+
}
61+
62+
const opServerUrl = Array.isArray(operation.servers) && operation.servers[0]?.url
63+
? String(operation.servers[0].url).replace(/\/$/, '')
64+
: undefined;
65+
const endpointBaseUrl = opServerUrl || pathServerUrl;
5166

5267
const name = operation.operationId || generateMethodName(httpMethod, path);
5368
const isPrivate = operation.security !== undefined
@@ -59,6 +74,7 @@ export function parseOpenApiSpec(spec: any, baseUrl?: string): ApiDescriptor {
5974
path,
6075
isPrivate,
6176
operationId: operation.operationId,
77+
...(endpointBaseUrl ? { baseUrl: endpointBaseUrl } : {}),
6278
};
6379
}
6480
}

‎core/test/unit/openapi.test.ts‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { polymarketClobSpec } from '../../src/exchanges/polymarket/api-clob';
2+
import { parseOpenApiSpec } from '../../src/utils/openapi';
3+
4+
describe('parseOpenApiSpec', () => {
5+
it('Polymarket getGeoblock uses website API host (not CLOB)', () => {
6+
const d = parseOpenApiSpec(polymarketClobSpec);
7+
expect(d.endpoints.getGeoblock.baseUrl).toBe('https://polymarket.com/api');
8+
expect(d.endpoints.getGeoblock.path).toBe('/geoblock');
9+
});
10+
11+
it('uses operation-level servers as endpoint baseUrl override', () => {
12+
const spec = {
13+
openapi: '3.0.3',
14+
servers: [{ url: 'https://primary.example.com' }],
15+
paths: {
16+
'/x': {
17+
get: {
18+
operationId: 'getX',
19+
servers: [{ url: 'https://override.example.com/' }],
20+
},
21+
},
22+
},
23+
};
24+
25+
const d = parseOpenApiSpec(spec);
26+
expect(d.baseUrl).toBe('https://primary.example.com');
27+
expect(d.endpoints.getX.baseUrl).toBe('https://override.example.com');
28+
});
29+
30+
it('inherits path-level servers when the operation omits servers', () => {
31+
const spec = {
32+
openapi: '3.0.3',
33+
servers: [{ url: 'https://primary.example.com' }],
34+
paths: {
35+
'/y': {
36+
servers: [{ url: 'https://path-level.example.com' }],
37+
get: { operationId: 'getY' },
38+
},
39+
},
40+
};
41+
42+
const d = parseOpenApiSpec(spec);
43+
expect(d.endpoints.getY.baseUrl).toBe('https://path-level.example.com');
44+
});
45+
});

0 commit comments

Comments
 (0)