mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-29 00:01:20 +00:00
feat: phase 4 integrations — connectors, rybbit analytics, webhooks
- core.integrations (migration 00005) with AES-256-GCM-encrypted credentials (PRIMORA_ENCRYPTION_KEY, hex/base64, dev fallback); has_credentials serialization only — secrets never leave the server - /projects/:id/integrations list/create/delete/test; health check hits the stored base URL with a 5s timeout and records status - Rybbit connector: GET …/integrations/:id/analytics proxies /api/organizations + /api/sites/* server-side into a normalized shape - core.webhooks + core.webhook_deliveries; CRUD, deliveries, test, and POST …/deploy-markers. Events: issue.created (new fingerprint on ingest), deploy.marker, webhook.test. X-Primora-Signature HMAC-SHA256 over the raw body; in-process dispatcher, 3 attempts, quadratic backoff, 10s timeout, pending-scan on boot - HTTPS enforced for public targets; HTTP allowed for loopback/private/ self-hosted sinks - Integrations page (connectors + webhooks tabs, inline analytics, delivery log, secret-shown-once), demo-mode stubs - 12 new primora_* MCP tools - workspace 0.4.0 Authored By: TDvorak <[email protected]>
This commit is contained in:
1 parent
8cfddf9184
commit
e25eb92339
82 files changed
+5710
-61
No files matched your search
@@ -0,0 +1,143 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CreateIntegrationRequest } from '../models/CreateIntegrationRequest';
|
||||
import type { Integration } from '../models/Integration';
|
||||
import type { IntegrationAnalytics } from '../models/IntegrationAnalytics';
|
||||
import type { IntegrationListResponse } from '../models/IntegrationListResponse';
|
||||
import type { IntegrationTestResult } from '../models/IntegrationTestResult';
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
export class IntegrationsService {
|
||||
/**
|
||||
* @returns IntegrationListResponse External service integrations for the project
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static listIntegrations({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: string,
|
||||
}): CancelablePromise<IntegrationListResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/projects/{projectID}/integrations',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
},
|
||||
errors: {
|
||||
401: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @returns Integration Integration created
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createIntegration({
|
||||
projectId,
|
||||
requestBody,
|
||||
}: {
|
||||
projectId: string,
|
||||
requestBody: CreateIntegrationRequest,
|
||||
}): CancelablePromise<Integration> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/projects/{projectID}/integrations',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
401: `Error response`,
|
||||
409: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @returns void
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteIntegration({
|
||||
projectId,
|
||||
integrationId,
|
||||
}: {
|
||||
projectId: string,
|
||||
integrationId: string,
|
||||
}): CancelablePromise<void> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'DELETE',
|
||||
url: '/projects/{projectID}/integrations/{integrationID}',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'integrationID': integrationId,
|
||||
},
|
||||
errors: {
|
||||
404: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Health check — HTTP GET against the integration's base URL with stored credentials (~5s timeout). Updates status and last_health_at.
|
||||
* @returns IntegrationTestResult Connectivity probe result
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static testIntegration({
|
||||
projectId,
|
||||
integrationId,
|
||||
}: {
|
||||
projectId: string,
|
||||
integrationId: string,
|
||||
}): CancelablePromise<IntegrationTestResult> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/projects/{projectID}/integrations/{integrationID}/test',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'integrationID': integrationId,
|
||||
},
|
||||
errors: {
|
||||
404: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Read-only analytics pulled from the integration (Rybbit). The API key never leaves the server.
|
||||
* @returns IntegrationAnalytics Normalized analytics payload
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getIntegrationAnalytics({
|
||||
projectId,
|
||||
integrationId,
|
||||
site,
|
||||
days = 30,
|
||||
}: {
|
||||
projectId: string,
|
||||
integrationId: string,
|
||||
/**
|
||||
* Rybbit site id override; defaults to the integration's configured site, else first accessible site.
|
||||
*/
|
||||
site?: string,
|
||||
days?: number,
|
||||
}): CancelablePromise<IntegrationAnalytics> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/projects/{projectID}/integrations/{integrationID}/analytics',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'integrationID': integrationId,
|
||||
},
|
||||
query: {
|
||||
'site': site,
|
||||
'days': days,
|
||||
},
|
||||
errors: {
|
||||
400: `Error response`,
|
||||
404: `Error response`,
|
||||
502: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CreateDeployMarkerRequest } from '../models/CreateDeployMarkerRequest';
|
||||
import type { CreateWebhookRequest } from '../models/CreateWebhookRequest';
|
||||
import type { DeployMarker } from '../models/DeployMarker';
|
||||
import type { UpdateWebhookRequest } from '../models/UpdateWebhookRequest';
|
||||
import type { WebhookCreateResponse } from '../models/WebhookCreateResponse';
|
||||
import type { WebhookDelivery } from '../models/WebhookDelivery';
|
||||
import type { WebhookDeliveryListResponse } from '../models/WebhookDeliveryListResponse';
|
||||
import type { WebhookListResponse } from '../models/WebhookListResponse';
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
export class WebhooksService {
|
||||
/**
|
||||
* @returns WebhookListResponse Outbound webhooks for the project
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static listWebhooks({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: string,
|
||||
}): CancelablePromise<WebhookListResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/projects/{projectID}/webhooks',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
},
|
||||
errors: {
|
||||
401: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Deliveries are POSTed as JSON with X-Primora-Event and X-Primora-Signature (sha256=<HMAC-SHA256 hex of the raw body, keyed by the webhook secret>). If no secret is supplied one is generated and returned once.
|
||||
* @returns WebhookCreateResponse Webhook created
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createWebhook({
|
||||
projectId,
|
||||
requestBody,
|
||||
}: {
|
||||
projectId: string,
|
||||
requestBody: CreateWebhookRequest,
|
||||
}): CancelablePromise<WebhookCreateResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/projects/{projectID}/webhooks',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
401: `Error response`,
|
||||
409: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @returns WebhookCreateResponse Webhook updated
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateWebhook({
|
||||
projectId,
|
||||
webhookId,
|
||||
requestBody,
|
||||
}: {
|
||||
projectId: string,
|
||||
webhookId: string,
|
||||
requestBody: UpdateWebhookRequest,
|
||||
}): CancelablePromise<WebhookCreateResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PATCH',
|
||||
url: '/projects/{projectID}/webhooks/{webhookID}',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'webhookID': webhookId,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
404: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @returns void
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteWebhook({
|
||||
projectId,
|
||||
webhookId,
|
||||
}: {
|
||||
projectId: string,
|
||||
webhookId: string,
|
||||
}): CancelablePromise<void> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'DELETE',
|
||||
url: '/projects/{projectID}/webhooks/{webhookID}',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'webhookID': webhookId,
|
||||
},
|
||||
errors: {
|
||||
404: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @returns WebhookDeliveryListResponse Recent delivery attempts, newest first
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static listWebhookDeliveries({
|
||||
projectId,
|
||||
webhookId,
|
||||
limit = 50,
|
||||
}: {
|
||||
projectId: string,
|
||||
webhookId: string,
|
||||
limit?: number,
|
||||
}): CancelablePromise<WebhookDeliveryListResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/projects/{projectID}/webhooks/{webhookID}/deliveries',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'webhookID': webhookId,
|
||||
},
|
||||
query: {
|
||||
'limit': limit,
|
||||
},
|
||||
errors: {
|
||||
404: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Queues a webhook.test ping delivery.
|
||||
* @returns WebhookDelivery Test delivery queued
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static testWebhook({
|
||||
projectId,
|
||||
webhookId,
|
||||
}: {
|
||||
projectId: string,
|
||||
webhookId: string,
|
||||
}): CancelablePromise<WebhookDelivery> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/projects/{projectID}/webhooks/{webhookID}/test',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
'webhookID': webhookId,
|
||||
},
|
||||
errors: {
|
||||
404: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Records a deployment marker in the audit log and fires the deploy.marker webhook event.
|
||||
* @returns DeployMarker Marker recorded
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createDeployMarker({
|
||||
projectId,
|
||||
requestBody,
|
||||
}: {
|
||||
projectId: string,
|
||||
requestBody: CreateDeployMarkerRequest,
|
||||
}): CancelablePromise<DeployMarker> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/projects/{projectID}/deploy-markers',
|
||||
path: {
|
||||
'projectID': projectId,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
401: `Error response`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user