Add OpenAPI import backend
This commit is contained in:
@@ -72,6 +72,8 @@ pub enum RegistryError {
|
||||
AuthProfileNotFound { auth_profile_id: String },
|
||||
#[error("yaml import job {job_id} was not found")]
|
||||
YamlImportJobNotFound { job_id: String },
|
||||
#[error("import job {job_id} was not found")]
|
||||
ImportJobNotFound { job_id: String },
|
||||
#[error("unsupported enum representation for field {field}")]
|
||||
InvalidEnumRepresentation { field: &'static str },
|
||||
#[error("invalid numeric value for field {field}: {value}")]
|
||||
|
||||
@@ -10,10 +10,11 @@ pub use ext::{ExtensionMigration, RegistryExtension, apply_extension_migrations}
|
||||
pub mod records {
|
||||
pub use crate::model::{
|
||||
AgentSummary, AgentVersionRecord, AuthUserRecord, DescriptorKind, DescriptorMetadata,
|
||||
InvitationRecord, InvocationLogRecord, MembershipRecord, OperationAgentRef,
|
||||
OperationSampleMetadata, OperationSummary, OperationUsageSummary, OperationVersionRecord,
|
||||
Page, PlatformApiKeyRecord, PublishedAgentTool, RegistryOperation, SampleKind,
|
||||
SecretRecord, SecretVersionRecord, SessionRecord, UsageAgentBreakdown, UsageBucket,
|
||||
ImportJob, ImportJobId, ImportJobKind, ImportJobStatus, InvitationRecord,
|
||||
InvocationLogRecord, MembershipRecord, OperationAgentRef, OperationSampleMetadata,
|
||||
OperationSummary, OperationUsageSummary, OperationVersionRecord, Page,
|
||||
PlatformApiKeyRecord, PublishedAgentTool, RegistryOperation, SampleKind, SecretRecord,
|
||||
SecretVersionRecord, SessionRecord, UsageAgentBreakdown, UsageBucket,
|
||||
UsageOperationBreakdown, UsageRollupRecord, UsageSummary, UsageTimelinePoint,
|
||||
WorkspaceMembershipRecord, WorkspaceRecord, WorkspaceUpstream, WorkspaceUpstreamId,
|
||||
YamlImportJob, YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
|
||||
@@ -22,13 +23,13 @@ pub mod records {
|
||||
|
||||
pub mod requests {
|
||||
pub use crate::model::{
|
||||
CreateAgentDraftVersionRequest, CreateAgentRequest, CreateInvitationRequest,
|
||||
CreateInvocationLogRequest, CreatePlatformApiKeyRequest, CreateSecretRequest,
|
||||
CreateVersionRequest, CreateWorkspaceRequest, CreateYamlImportJobRequest,
|
||||
ListInvocationLogsQuery, PublishAgentRequest, PublishRequest, RotateSecretRequest,
|
||||
SaveAgentBindingsRequest, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
|
||||
SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest, UpdateWorkspaceRequest,
|
||||
UsageQuery,
|
||||
CreateAgentDraftVersionRequest, CreateAgentRequest, CreateImportJobRequest,
|
||||
CreateInvitationRequest, CreateInvocationLogRequest, CreatePlatformApiKeyRequest,
|
||||
CreateSecretRequest, CreateVersionRequest, CreateWorkspaceRequest,
|
||||
CreateYamlImportJobRequest, FinishImportJobRequest, ListInvocationLogsQuery,
|
||||
PublishAgentRequest, PublishRequest, RotateSecretRequest, SaveAgentBindingsRequest,
|
||||
SaveAuthProfileRequest, SaveDescriptorMetadataRequest, SaveSampleMetadataRequest,
|
||||
SaveWorkspaceUpstreamRequest, UpdateWorkspaceRequest, UsageQuery,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,18 +40,19 @@ pub mod infrastructure {
|
||||
|
||||
pub use model::{
|
||||
AgentSummary, AgentVersionRecord, AuthUserRecord, CreateAgentDraftVersionRequest,
|
||||
CreateAgentRequest, CreateInvitationRequest, CreateInvocationLogRequest,
|
||||
CreatePlatformApiKeyRequest, CreateSecretRequest, CreateVersionRequest, CreateWorkspaceRequest,
|
||||
CreateYamlImportJobRequest, DescriptorKind, DescriptorMetadata, InvitationRecord,
|
||||
InvocationLogRecord, ListInvocationLogsQuery, MembershipRecord, OperationAgentRef,
|
||||
OperationSampleMetadata, OperationSummary, OperationUsageSummary, OperationVersionRecord, Page,
|
||||
PlatformApiKeyRecord, PublishAgentRequest, PublishRequest, PublishedAgentTool,
|
||||
RegistryOperation, RotateSecretRequest, SampleKind, SaveAgentBindingsRequest,
|
||||
SaveAuthProfileRequest, SaveDescriptorMetadataRequest, SaveSampleMetadataRequest,
|
||||
SaveWorkspaceUpstreamRequest, SecretRecord, SecretVersionRecord, SessionRecord,
|
||||
UpdateWorkspaceRequest, UsageAgentBreakdown, UsageBucket, UsageOperationBreakdown, UsageQuery,
|
||||
UsageRollupRecord, UsageSummary, UsageTimelinePoint, WorkspaceMembershipRecord,
|
||||
WorkspaceRecord, WorkspaceUpstream, WorkspaceUpstreamId, YamlImportJob,
|
||||
YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
|
||||
CreateAgentRequest, CreateImportJobRequest, CreateInvitationRequest,
|
||||
CreateInvocationLogRequest, CreatePlatformApiKeyRequest, CreateSecretRequest,
|
||||
CreateVersionRequest, CreateWorkspaceRequest, CreateYamlImportJobRequest, DescriptorKind,
|
||||
DescriptorMetadata, FinishImportJobRequest, ImportJob, ImportJobId, ImportJobKind,
|
||||
ImportJobStatus, InvitationRecord, InvocationLogRecord, ListInvocationLogsQuery,
|
||||
MembershipRecord, OperationAgentRef, OperationSampleMetadata, OperationSummary,
|
||||
OperationUsageSummary, OperationVersionRecord, Page, PlatformApiKeyRecord, PublishAgentRequest,
|
||||
PublishRequest, PublishedAgentTool, RegistryOperation, RotateSecretRequest, SampleKind,
|
||||
SaveAgentBindingsRequest, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
|
||||
SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest, SecretRecord, SecretVersionRecord,
|
||||
SessionRecord, UpdateWorkspaceRequest, UsageAgentBreakdown, UsageBucket,
|
||||
UsageOperationBreakdown, UsageQuery, UsageRollupRecord, UsageSummary, UsageTimelinePoint,
|
||||
WorkspaceMembershipRecord, WorkspaceRecord, WorkspaceUpstream, WorkspaceUpstreamId,
|
||||
YamlImportJob, YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
|
||||
};
|
||||
pub use postgres::{PostgresPoolConfig, PostgresPoolConfigError, PostgresRegistry};
|
||||
|
||||
@@ -451,6 +451,25 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
"create table if not exists import_jobs (
|
||||
id text primary key,
|
||||
workspace_id text not null references workspaces(id) on delete cascade,
|
||||
kind text not null,
|
||||
source_format text not null,
|
||||
source_version text null,
|
||||
status text not null,
|
||||
preview_payload jsonb not null,
|
||||
created_operation_ids jsonb not null default '[]'::jsonb,
|
||||
error_text text null,
|
||||
created_at timestamptz not null,
|
||||
expires_at timestamptz not null,
|
||||
finished_at timestamptz null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
"create table if not exists agents (
|
||||
id text primary key,
|
||||
|
||||
@@ -41,6 +41,7 @@ macro_rules! define_registry_id {
|
||||
}
|
||||
|
||||
define_registry_id!(YamlImportJobId);
|
||||
define_registry_id!(ImportJobId);
|
||||
define_registry_id!(WorkspaceUpstreamId);
|
||||
|
||||
pub type RegistryOperation = Operation<Schema, MappingSet>;
|
||||
@@ -326,6 +327,61 @@ pub struct YamlImportJobCompletion {
|
||||
pub finished_at: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ImportJobStatus {
|
||||
Pending,
|
||||
Completed,
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ImportJobKind {
|
||||
OpenApi,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ImportJob {
|
||||
pub id: ImportJobId,
|
||||
pub workspace_id: WorkspaceId,
|
||||
pub kind: ImportJobKind,
|
||||
pub source_format: String,
|
||||
pub source_version: Option<String>,
|
||||
pub status: ImportJobStatus,
|
||||
pub preview_payload: Value,
|
||||
pub created_operation_ids: Value,
|
||||
pub error_text: Option<String>,
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub created_at: OffsetDateTime,
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub expires_at: OffsetDateTime,
|
||||
#[serde(with = "time::serde::rfc3339::option")]
|
||||
pub finished_at: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CreateImportJobRequest<'a> {
|
||||
pub id: &'a ImportJobId,
|
||||
pub workspace_id: &'a WorkspaceId,
|
||||
pub kind: ImportJobKind,
|
||||
pub source_format: &'a str,
|
||||
pub source_version: Option<&'a str>,
|
||||
pub status: ImportJobStatus,
|
||||
pub preview_payload: &'a Value,
|
||||
pub created_at: &'a OffsetDateTime,
|
||||
pub expires_at: &'a OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FinishImportJobRequest<'a> {
|
||||
pub id: &'a ImportJobId,
|
||||
pub status: ImportJobStatus,
|
||||
pub created_operation_ids: &'a Value,
|
||||
pub error_text: Option<&'a str>,
|
||||
pub finished_at: &'a OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CreateInvocationLogRequest<'a> {
|
||||
pub log: &'a InvocationLog,
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
use super::*;
|
||||
|
||||
impl PostgresRegistry {
|
||||
pub async fn create_import_job(
|
||||
&self,
|
||||
request: CreateImportJobRequest<'_>,
|
||||
) -> Result<(), RegistryError> {
|
||||
sqlx::query(
|
||||
"insert into import_jobs (
|
||||
id,
|
||||
workspace_id,
|
||||
kind,
|
||||
source_format,
|
||||
source_version,
|
||||
status,
|
||||
preview_payload,
|
||||
created_operation_ids,
|
||||
error_text,
|
||||
created_at,
|
||||
expires_at,
|
||||
finished_at
|
||||
) values (
|
||||
$1, $2, $3, $4, $5, $6, $7, '[]'::jsonb, null, $8::timestamptz, $9::timestamptz, null
|
||||
)",
|
||||
)
|
||||
.bind(request.id.as_str())
|
||||
.bind(request.workspace_id.as_str())
|
||||
.bind(serialize_enum_text(&request.kind, "kind")?)
|
||||
.bind(request.source_format)
|
||||
.bind(request.source_version)
|
||||
.bind(serialize_enum_text(&request.status, "status")?)
|
||||
.bind(request.preview_payload)
|
||||
.bind(request.created_at)
|
||||
.bind(request.expires_at)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_import_job(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
job_id: &ImportJobId,
|
||||
) -> Result<Option<ImportJob>, RegistryError> {
|
||||
let row = sqlx::query(
|
||||
"select
|
||||
id,
|
||||
workspace_id,
|
||||
kind,
|
||||
source_format,
|
||||
source_version,
|
||||
status,
|
||||
preview_payload,
|
||||
created_operation_ids,
|
||||
error_text,
|
||||
created_at,
|
||||
expires_at,
|
||||
finished_at
|
||||
from import_jobs
|
||||
where id = $1 and workspace_id = $2",
|
||||
)
|
||||
.bind(job_id.as_str())
|
||||
.bind(workspace_id.as_str())
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
|
||||
row.as_ref().map(map_import_job).transpose()
|
||||
}
|
||||
|
||||
pub async fn finish_import_job(
|
||||
&self,
|
||||
request: FinishImportJobRequest<'_>,
|
||||
) -> Result<(), RegistryError> {
|
||||
let result = sqlx::query(
|
||||
"update import_jobs
|
||||
set status = $2,
|
||||
created_operation_ids = $3,
|
||||
error_text = $4,
|
||||
finished_at = $5::timestamptz
|
||||
where id = $1",
|
||||
)
|
||||
.bind(request.id.as_str())
|
||||
.bind(serialize_enum_text(&request.status, "status")?)
|
||||
.bind(request.created_operation_ids)
|
||||
.bind(request.error_text)
|
||||
.bind(request.finished_at)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(RegistryError::ImportJobNotFound {
|
||||
job_id: request.id.as_str().to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_expired_import_jobs(&self) -> Result<u64, RegistryError> {
|
||||
let result = sqlx::query("delete from import_jobs where expires_at < now()")
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ mod agent;
|
||||
mod api_key;
|
||||
mod auth;
|
||||
mod connection;
|
||||
mod import_job;
|
||||
mod observability;
|
||||
mod operation;
|
||||
mod operation_artifact;
|
||||
@@ -28,9 +29,10 @@ use crate::{
|
||||
error::RegistryError,
|
||||
model::{
|
||||
AgentSummary, AgentVersionRecord, AuthUserRecord, CreateAgentDraftVersionRequest,
|
||||
CreateAgentRequest, CreateInvitationRequest, CreateInvocationLogRequest,
|
||||
CreatePlatformApiKeyRequest, CreateSecretRequest, CreateVersionRequest,
|
||||
CreateWorkspaceRequest, CreateYamlImportJobRequest, DescriptorMetadata, InvitationRecord,
|
||||
CreateAgentRequest, CreateImportJobRequest, CreateInvitationRequest,
|
||||
CreateInvocationLogRequest, CreatePlatformApiKeyRequest, CreateSecretRequest,
|
||||
CreateVersionRequest, CreateWorkspaceRequest, CreateYamlImportJobRequest,
|
||||
DescriptorMetadata, FinishImportJobRequest, ImportJob, ImportJobId, InvitationRecord,
|
||||
InvocationLogRecord, ListInvocationLogsQuery, MembershipRecord, OperationAgentRef,
|
||||
OperationSampleMetadata, OperationSummary, OperationUsageSummary, OperationVersionRecord,
|
||||
PlatformApiKeyRecord, PublishAgentRequest, PublishRequest, PublishedAgentTool,
|
||||
@@ -661,6 +663,23 @@ fn map_yaml_import_job(row: &PgRow) -> Result<YamlImportJob, RegistryError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn map_import_job(row: &PgRow) -> Result<ImportJob, RegistryError> {
|
||||
Ok(ImportJob {
|
||||
id: ImportJobId::new(row.try_get::<String, _>("id")?),
|
||||
workspace_id: WorkspaceId::new(row.try_get::<String, _>("workspace_id")?),
|
||||
kind: deserialize_enum_text(&row.try_get::<String, _>("kind")?, "kind")?,
|
||||
source_format: row.try_get("source_format")?,
|
||||
source_version: row.try_get("source_version")?,
|
||||
status: deserialize_enum_text(&row.try_get::<String, _>("status")?, "status")?,
|
||||
preview_payload: row.try_get("preview_payload")?,
|
||||
created_operation_ids: row.try_get("created_operation_ids")?,
|
||||
error_text: row.try_get("error_text")?,
|
||||
created_at: row.try_get("created_at")?,
|
||||
expires_at: row.try_get("expires_at")?,
|
||||
finished_at: row.try_get("finished_at")?,
|
||||
})
|
||||
}
|
||||
|
||||
fn map_usage_operation_breakdown(row: &PgRow) -> Result<UsageOperationBreakdown, RegistryError> {
|
||||
Ok(UsageOperationBreakdown {
|
||||
operation_id: OperationId::new(row.try_get::<String, _>("operation_id")?),
|
||||
|
||||
@@ -156,6 +156,7 @@ pub(super) fn test_operation(id: &str, version: u32, status: OperationStatus) ->
|
||||
input_sample: Some(json!({ "email": format!("lead-{version}@example.com") })),
|
||||
output_sample: Some(json!({ "id": format!("lead_{version}") })),
|
||||
test_input: Some(json!({ "email": format!("test-v{version}@example.com") })),
|
||||
import_findings: Vec::new(),
|
||||
}),
|
||||
created_at: timestamp("2026-03-25T11:58:00Z"),
|
||||
updated_at: timestamp(&format!("2026-03-25T12:{version:02}:00Z")),
|
||||
|
||||
Reference in New Issue
Block a user