Date : 25/01/2026 Status : PROPOSITION Projet : connectors-api
request_schema pour POST/PUT/api/discover/:idQuand un connecteur est créé :
POST /api/connectors → create() → triggerAutoDiscovery()
Le flow :
swagger_url fourni → importer directementEssayer ces URLs dans l'ordre :
const SWAGGER_PATHS = [
'/swagger.json',
'/openapi.json',
'/api-docs',
'/v3/api-docs',
'/swagger/v1/swagger.json',
'/api/swagger.json',
'/.well-known/openapi.json',
'/docs/openapi.json',
];
export const autodiscovery = {
/**
* Find swagger URL for a connector
*/
async findSwaggerUrl(baseUrl: string): Promise<string | null> {
for (const path of SWAGGER_PATHS) {
const url = `${baseUrl}${path}`;
try {
const res = await fetch(url, { method: 'HEAD' });
if (res.ok) {
// Verify it's JSON
const contentType = res.headers.get('content-type');
if (contentType?.includes('json')) {
return url;
}
}
} catch {}
}
return null;
},
/**
* Trigger full discovery for a connector
*/
async triggerDiscovery(
connector: ConnectorConfig,
options?: {
userId?: string;
skipIfExists?: boolean;
exploreFallback?: boolean;
}
): Promise<DiscoveryResult> {
// 1. Check if already discovered
if (options?.skipIfExists) {
const existing = await discovery.getEndpoints(connector.id);
if (existing.length > 0) {
return { status: 'skipped', message: 'Already discovered', count: existing.length };
}
}
// 2. Try swagger_url from config
if (connector.swagger_url) {
try {
const count = await discovery.importOpenAPIFromUrl(connector, connector.swagger_url);
return { status: 'success', source: 'swagger_url', count };
} catch (e) {
console.warn(`Failed to import from swagger_url: ${e}`);
}
}
// 3. Search for swagger
const foundUrl = await this.findSwaggerUrl(connector.base_url);
if (foundUrl) {
try {
const count = await discovery.importOpenAPIFromUrl(connector, foundUrl);
// Save found URL
await registry.update(connector.id, { swagger_url: foundUrl });
return { status: 'success', source: 'auto_detected', swagger_url: foundUrl, count };
} catch (e) {
console.warn(`Failed to import from detected swagger: ${e}`);
}
}
// 4. Fallback: explore common endpoints
if (options?.exploreFallback) {
const discovered = await this.exploreCommonEndpoints(connector, options.userId);
return { status: 'partial', source: 'exploration', count: discovered.length };
}
return { status: 'failed', message: 'No swagger found and exploration disabled' };
},
/**
* Explore common endpoints when no swagger available
*/
async exploreCommonEndpoints(connector: ConnectorConfig, userId?: string): Promise<string[]> {
const commonPaths = [
'/user', '/users', '/me',
'/projects', '/repos', '/repositories',
'/issues', '/items', '/resources',
'/organizations', '/orgs', '/teams',
'/domains', '/accounts', '/contacts'
];
const discovered: string[] = [];
for (const path of commonPaths) {
try {
await discovery.discoverEndpoint(connector, 'GET', path, userId, 1);
discovered.push(path);
} catch {}
}
return discovered;
}
};
async create(config: Partial<ConnectorConfig>): Promise<ConnectorConfig> {
// Create connector
const connector = await supabase.insert<ConnectorConfig>('config', {...});
// Trigger auto-discovery in background (don't block)
autodiscovery.triggerDiscovery(connector, {
exploreFallback: true
}).then(result => {
console.log(`Auto-discovery for ${connector.name}:`, result);
}).catch(e => {
console.error(`Auto-discovery failed for ${connector.name}:`, e);
});
return connector;
}
Ajouter au type ConnectorConfig :
discovery_status: 'pending' | 'in_progress' | 'completed' | 'failed' | null;
discovery_error: string | null;
endpoints_count: number;
POST /api/connectors/:id/rediscover
Force une nouvelle découverte (efface les endpoints existants).
| Fichier | Modification |
|---|---|
src/types/connector.ts |
Ajouter discovery_status, discovery_error, endpoints_count |
src/services/autodiscovery.ts |
NOUVEAU - Service auto-discovery |
src/services/registry.ts |
Appeler autodiscovery.triggerDiscovery() dans create() |
src/index.ts |
Ajouter endpoint /api/connectors/:id/rediscover |
| Connecteur | Swagger URL | Taille | Notes |
|---|---|---|---|
| GitHub | github.com/rest-api-description | 11 Mo | Nécessite extraction |
| GitLab | gitlab.com/api/v4 swagger | ~5 Mo | OpenAPI 3.0 |
| Mailjet | dev.mailjet.com/email/reference | N/A | Pas d'OpenAPI public |