Skip to content

Commit 12d213c

Browse files
authored
Merge pull request #109 from objectstack-ai/copilot/implement-advanced-router-middleware
2 parents bc38302 + 6db7b68 commit 12d213c

28 files changed

+15266
-0
lines changed

IMPLEMENTATION_SUMMARY.md

Lines changed: 423 additions & 0 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 9594 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/**
2+
* API Discovery Service
3+
*
4+
* Provides dynamic API documentation and metadata
5+
*/
6+
7+
import { Router, RouteMetadata } from './router';
8+
import { EndpointRegistry } from './endpoint-registry';
9+
10+
/**
11+
* System capabilities
12+
*/
13+
export interface SystemCapabilities {
14+
/** GraphQL endpoint available */
15+
graphql?: boolean;
16+
/** WebSocket support */
17+
websocket?: boolean;
18+
/** File upload support */
19+
fileUpload?: boolean;
20+
/** Batch operations support */
21+
batch?: boolean;
22+
/** Real-time subscriptions */
23+
realtime?: boolean;
24+
}
25+
26+
/**
27+
* API version info
28+
*/
29+
export interface ApiVersionInfo {
30+
version: string;
31+
releaseDate?: string;
32+
deprecated?: boolean;
33+
sunsetDate?: string;
34+
}
35+
36+
/**
37+
* Discovery response
38+
*/
39+
export interface DiscoveryResponse {
40+
/** API name */
41+
name: string;
42+
/** API version */
43+
version: ApiVersionInfo;
44+
/** API description */
45+
description?: string;
46+
/** Base URL */
47+
baseUrl?: string;
48+
/** System capabilities */
49+
capabilities: SystemCapabilities;
50+
/** Available routes */
51+
routes: RouteMetadata[];
52+
/** API endpoints (from endpoint registry) */
53+
endpoints?: any[];
54+
/** Environment info */
55+
environment?: {
56+
name: string;
57+
region?: string;
58+
[key: string]: any;
59+
};
60+
/** Contact info */
61+
contact?: {
62+
name?: string;
63+
email?: string;
64+
url?: string;
65+
};
66+
/** Links to documentation */
67+
links?: {
68+
documentation?: string;
69+
openapi?: string;
70+
support?: string;
71+
};
72+
}
73+
74+
/**
75+
* Discovery configuration
76+
*/
77+
export interface DiscoveryConfig {
78+
name: string;
79+
version: string;
80+
description?: string;
81+
baseUrl?: string;
82+
capabilities?: SystemCapabilities;
83+
environment?: {
84+
name: string;
85+
region?: string;
86+
[key: string]: any;
87+
};
88+
contact?: {
89+
name?: string;
90+
email?: string;
91+
url?: string;
92+
};
93+
}
94+
95+
/**
96+
* API Discovery Service
97+
*/
98+
export class DiscoveryService {
99+
private router: Router;
100+
private endpointRegistry?: EndpointRegistry;
101+
private config: DiscoveryConfig;
102+
103+
constructor(
104+
router: Router,
105+
config: DiscoveryConfig,
106+
endpointRegistry?: EndpointRegistry
107+
) {
108+
this.router = router;
109+
this.config = config;
110+
this.endpointRegistry = endpointRegistry;
111+
}
112+
113+
/**
114+
* Get discovery information
115+
*/
116+
getDiscovery(): DiscoveryResponse {
117+
const routes = this.router.getRoutes();
118+
const endpoints = this.endpointRegistry?.listEndpoints();
119+
120+
const discovery: DiscoveryResponse = {
121+
name: this.config.name,
122+
version: {
123+
version: this.config.version,
124+
},
125+
description: this.config.description,
126+
baseUrl: this.config.baseUrl,
127+
capabilities: this.config.capabilities || {},
128+
routes: this.filterSystemRoutes(routes),
129+
environment: this.config.environment,
130+
contact: this.config.contact,
131+
links: {
132+
documentation: `${this.config.baseUrl || ''}/docs`,
133+
openapi: `${this.config.baseUrl || ''}/api/openapi`,
134+
},
135+
};
136+
137+
if (endpoints) {
138+
discovery.endpoints = endpoints;
139+
}
140+
141+
return discovery;
142+
}
143+
144+
/**
145+
* Get routes by category
146+
*/
147+
getRoutesByCategory(category: string): RouteMetadata[] {
148+
return this.router.getRoutesByCategory(category as any);
149+
}
150+
151+
/**
152+
* Get routes by tag
153+
*/
154+
getRoutesByTag(tag: string): RouteMetadata[] {
155+
return this.router.getRoutesByTag(tag);
156+
}
157+
158+
/**
159+
* Get route statistics
160+
*/
161+
getRouteStats(): any {
162+
const routes = this.router.getRoutes();
163+
164+
const stats = {
165+
total: routes.length,
166+
byMethod: {} as Record<string, number>,
167+
byCategory: {} as Record<string, number>,
168+
withRateLimit: 0,
169+
deprecated: 0,
170+
};
171+
172+
for (const route of routes) {
173+
// Count by method
174+
stats.byMethod[route.method] = (stats.byMethod[route.method] || 0) + 1;
175+
176+
// Count by category
177+
stats.byCategory[route.category] = (stats.byCategory[route.category] || 0) + 1;
178+
179+
// Count rate limited routes
180+
if (route.rateLimit) {
181+
stats.withRateLimit++;
182+
}
183+
184+
// Count deprecated routes
185+
if (route.deprecated) {
186+
stats.deprecated++;
187+
}
188+
}
189+
190+
return stats;
191+
}
192+
193+
/**
194+
* Filter out system routes from discovery
195+
*/
196+
private filterSystemRoutes(routes: RouteMetadata[]): RouteMetadata[] {
197+
// Include all routes but optionally filter some internal ones
198+
// For now, keep all routes including system ones
199+
return routes;
200+
}
201+
202+
/**
203+
* Update configuration
204+
*/
205+
updateConfig(updates: Partial<DiscoveryConfig>): void {
206+
this.config = { ...this.config, ...updates };
207+
}
208+
}
209+
210+
/**
211+
* Create discovery service
212+
*/
213+
export function createDiscoveryService(
214+
router: Router,
215+
config: DiscoveryConfig,
216+
endpointRegistry?: EndpointRegistry
217+
): DiscoveryService {
218+
return new DiscoveryService(router, config, endpointRegistry);
219+
}
220+
221+
/**
222+
* Register discovery endpoint on router
223+
*/
224+
export function registerDiscoveryEndpoint(
225+
router: Router,
226+
discovery: DiscoveryService
227+
): void {
228+
router.get('/api/discovery', async (req: any, res: any) => {
229+
const discoveryInfo = discovery.getDiscovery();
230+
231+
if (res.json) {
232+
res.json(discoveryInfo);
233+
}
234+
}, {
235+
category: 'system',
236+
summary: 'API Discovery',
237+
description: 'Get information about available API endpoints',
238+
tags: ['system', 'discovery'],
239+
});
240+
241+
router.get('/api/discovery/stats', async (req: any, res: any) => {
242+
const stats = discovery.getRouteStats();
243+
244+
if (res.json) {
245+
res.json(stats);
246+
}
247+
}, {
248+
category: 'system',
249+
summary: 'API Statistics',
250+
description: 'Get statistics about API routes',
251+
tags: ['system', 'discovery'],
252+
});
253+
}

0 commit comments

Comments
 (0)