新增 Web Project runtime job、持久日志、lease、取消、expired、stale 和 active preview guard 状态机 接入 api-server Web Project runtime worker 与 TempDirBuildRuntime 构建执行链路 补齐 SpacetimeDB procedure、spacetime-client facade、shared contracts 和前端 web-project client 契约 更新 /editor/agent 的 runtime job 恢复、日志回填、SSE 重连、取消按钮和 active preview 刷新恢复 新增 P2 dev smoke 脚本,并让完整 npm run dev 默认以 all 角色启动 P2 worker 补充 P2 自动化测试、浏览器 smoke 验收记录、开发运维文档和 Hermes 踩坑记忆
949 lines
31 KiB
Rust
949 lines
31 KiB
Rust
use super::*;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectFileRecord {
|
|
pub path: String,
|
|
pub content: String,
|
|
pub media_type: String,
|
|
pub encoding: String,
|
|
pub size_bytes: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectRecord {
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
pub title: String,
|
|
pub template_key: String,
|
|
pub active_snapshot_id: String,
|
|
pub active_preview_build_id: Option<String>,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectSnapshotRecord {
|
|
pub snapshot_id: String,
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
pub parent_snapshot_id: Option<String>,
|
|
pub template_key: String,
|
|
pub files: Vec<WebProjectFileRecord>,
|
|
pub patch_summary: String,
|
|
pub created_by: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectPreviewBuildRecord {
|
|
pub job_id: String,
|
|
pub project_id: String,
|
|
pub snapshot_id: String,
|
|
pub owner_user_id: String,
|
|
pub status: String,
|
|
pub logs: Vec<String>,
|
|
pub artifact_id: Option<String>,
|
|
pub preview_token_id: Option<String>,
|
|
pub preview_url: Option<String>,
|
|
pub error_summary: Option<String>,
|
|
pub created_at: String,
|
|
pub started_at: Option<String>,
|
|
pub finished_at: Option<String>,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectRuntimeJobRecord {
|
|
pub job_id: String,
|
|
pub project_id: String,
|
|
pub snapshot_id: String,
|
|
pub owner_user_id: String,
|
|
pub job_kind: String,
|
|
pub status: String,
|
|
pub attempt: u32,
|
|
pub worker_id: Option<String>,
|
|
pub lease_token: Option<String>,
|
|
pub lease_expires_at: Option<String>,
|
|
pub cancel_requested_at: Option<String>,
|
|
pub stale_reason: Option<String>,
|
|
pub artifact_id: Option<String>,
|
|
pub preview_build_id: Option<String>,
|
|
pub error_summary: Option<String>,
|
|
pub created_at: String,
|
|
pub started_at: Option<String>,
|
|
pub finished_at: Option<String>,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectRuntimeJobLogRecord {
|
|
pub log_id: String,
|
|
pub job_id: String,
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
pub sequence: u64,
|
|
pub level: String,
|
|
pub message: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectCreateRecordInput {
|
|
pub project_id: String,
|
|
pub snapshot_id: String,
|
|
pub owner_user_id: String,
|
|
pub title: String,
|
|
pub initial_files: Vec<WebProjectFileRecord>,
|
|
pub now_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectGetRecordInput {
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectSnapshotGetRecordInput {
|
|
pub project_id: String,
|
|
pub snapshot_id: Option<String>,
|
|
pub owner_user_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectSnapshotSaveRecordInput {
|
|
pub snapshot_id: String,
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
pub parent_snapshot_id: Option<String>,
|
|
pub files: Vec<WebProjectFileRecord>,
|
|
pub patch_summary: String,
|
|
pub created_by: String,
|
|
pub now_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectPreviewBuildCreateRecordInput {
|
|
pub job_id: String,
|
|
pub project_id: String,
|
|
pub snapshot_id: String,
|
|
pub owner_user_id: String,
|
|
pub now_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectPreviewBuildGetRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectPreviewBuildTokenGetRecordInput {
|
|
pub preview_token_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectPreviewBuildUpdateRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
pub status: String,
|
|
pub logs: Vec<String>,
|
|
pub artifact_id: Option<String>,
|
|
pub preview_token_id: Option<String>,
|
|
pub preview_url: Option<String>,
|
|
pub error_summary: Option<String>,
|
|
pub started_at_micros: Option<i64>,
|
|
pub finished_at_micros: Option<i64>,
|
|
pub updated_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobCreateRecordInput {
|
|
pub job_id: String,
|
|
pub project_id: String,
|
|
pub snapshot_id: String,
|
|
pub owner_user_id: String,
|
|
pub job_kind: String,
|
|
pub preview_build_id: Option<String>,
|
|
pub now_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobGetRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobListOpenRecordInput {
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
pub limit: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobClaimRecordInput {
|
|
pub worker_id: String,
|
|
pub limit: u32,
|
|
pub lease_expires_at_micros: i64,
|
|
pub claimed_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobRenewLeaseRecordInput {
|
|
pub job_id: String,
|
|
pub worker_id: String,
|
|
pub lease_token: String,
|
|
pub lease_expires_at_micros: i64,
|
|
pub renewed_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobCompleteRecordInput {
|
|
pub job_id: String,
|
|
pub worker_id: String,
|
|
pub lease_token: String,
|
|
pub artifact_id: Option<String>,
|
|
pub preview_build_id: Option<String>,
|
|
pub completed_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobCompletePreviewBuildRecordInput {
|
|
pub job_id: String,
|
|
pub worker_id: String,
|
|
pub lease_token: String,
|
|
pub preview_build_id: String,
|
|
pub artifact_id: String,
|
|
pub preview_token_id: String,
|
|
pub preview_url: String,
|
|
pub logs: Vec<String>,
|
|
pub started_at_micros: Option<i64>,
|
|
pub finished_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobFailRecordInput {
|
|
pub job_id: String,
|
|
pub worker_id: String,
|
|
pub lease_token: String,
|
|
pub error_summary: String,
|
|
pub failed_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobCancelRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
pub cancelled_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobStaleRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
pub stale_reason: String,
|
|
pub stale_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobExpireRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
pub expired_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobAppendLogRecordInput {
|
|
pub log_id: String,
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
pub sequence: u64,
|
|
pub level: String,
|
|
pub message: String,
|
|
pub worker_id: Option<String>,
|
|
pub lease_token: Option<String>,
|
|
pub created_at_micros: i64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct WebProjectRuntimeJobListLogsRecordInput {
|
|
pub job_id: String,
|
|
pub owner_user_id: String,
|
|
pub after_sequence: Option<u64>,
|
|
pub limit: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectSnapshotMutationRecord {
|
|
pub project: WebProjectRecord,
|
|
pub snapshot: WebProjectSnapshotRecord,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectPreviewBuildMutationRecord {
|
|
pub project: WebProjectRecord,
|
|
pub build: WebProjectPreviewBuildRecord,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub struct WebProjectRuntimeJobPreviewBuildMutationRecord {
|
|
pub project: WebProjectRecord,
|
|
pub build: WebProjectPreviewBuildRecord,
|
|
pub job: WebProjectRuntimeJobRecord,
|
|
}
|
|
|
|
impl From<WebProjectFileRecord> for crate::module_bindings::WebProjectFileSnapshot {
|
|
fn from(input: WebProjectFileRecord) -> Self {
|
|
Self {
|
|
path: input.path,
|
|
content: input.content,
|
|
media_type: input.media_type,
|
|
encoding: input.encoding,
|
|
size_bytes: input.size_bytes,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::module_bindings::WebProjectFileSnapshot> for WebProjectFileRecord {
|
|
fn from(snapshot: crate::module_bindings::WebProjectFileSnapshot) -> Self {
|
|
Self {
|
|
path: snapshot.path,
|
|
content: snapshot.content,
|
|
media_type: snapshot.media_type,
|
|
encoding: snapshot.encoding,
|
|
size_bytes: snapshot.size_bytes,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectCreateRecordInput> for crate::module_bindings::WebProjectCreateInput {
|
|
fn from(input: WebProjectCreateRecordInput) -> Self {
|
|
Self {
|
|
project_id: input.project_id,
|
|
snapshot_id: input.snapshot_id,
|
|
owner_user_id: input.owner_user_id,
|
|
title: input.title,
|
|
initial_files: input.initial_files.into_iter().map(Into::into).collect(),
|
|
now_micros: input.now_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectGetRecordInput> for crate::module_bindings::WebProjectGetInput {
|
|
fn from(input: WebProjectGetRecordInput) -> Self {
|
|
Self {
|
|
project_id: input.project_id,
|
|
owner_user_id: input.owner_user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectSnapshotGetRecordInput> for crate::module_bindings::WebProjectSnapshotGetInput {
|
|
fn from(input: WebProjectSnapshotGetRecordInput) -> Self {
|
|
Self {
|
|
project_id: input.project_id,
|
|
snapshot_id: input.snapshot_id,
|
|
owner_user_id: input.owner_user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectSnapshotSaveRecordInput>
|
|
for crate::module_bindings::WebProjectSnapshotSaveInput
|
|
{
|
|
fn from(input: WebProjectSnapshotSaveRecordInput) -> Self {
|
|
Self {
|
|
snapshot_id: input.snapshot_id,
|
|
project_id: input.project_id,
|
|
owner_user_id: input.owner_user_id,
|
|
parent_snapshot_id: input.parent_snapshot_id,
|
|
files: input.files.into_iter().map(Into::into).collect(),
|
|
patch_summary: input.patch_summary,
|
|
created_by: input.created_by,
|
|
now_micros: input.now_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectPreviewBuildCreateRecordInput>
|
|
for crate::module_bindings::WebProjectPreviewBuildCreateInput
|
|
{
|
|
fn from(input: WebProjectPreviewBuildCreateRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
project_id: input.project_id,
|
|
snapshot_id: input.snapshot_id,
|
|
owner_user_id: input.owner_user_id,
|
|
now_micros: input.now_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectPreviewBuildGetRecordInput>
|
|
for crate::module_bindings::WebProjectPreviewBuildGetInput
|
|
{
|
|
fn from(input: WebProjectPreviewBuildGetRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectPreviewBuildTokenGetRecordInput>
|
|
for crate::module_bindings::WebProjectPreviewBuildTokenGetInput
|
|
{
|
|
fn from(input: WebProjectPreviewBuildTokenGetRecordInput) -> Self {
|
|
Self {
|
|
preview_token_id: input.preview_token_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectPreviewBuildUpdateRecordInput>
|
|
for crate::module_bindings::WebProjectPreviewBuildUpdateInput
|
|
{
|
|
fn from(input: WebProjectPreviewBuildUpdateRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
status: input.status,
|
|
logs: input.logs,
|
|
artifact_id: input.artifact_id,
|
|
preview_token_id: input.preview_token_id,
|
|
preview_url: input.preview_url,
|
|
error_summary: input.error_summary,
|
|
started_at_micros: input.started_at_micros,
|
|
finished_at_micros: input.finished_at_micros,
|
|
updated_at_micros: input.updated_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobCreateRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobCreateInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobCreateRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
project_id: input.project_id,
|
|
snapshot_id: input.snapshot_id,
|
|
owner_user_id: input.owner_user_id,
|
|
job_kind: input.job_kind,
|
|
preview_build_id: input.preview_build_id,
|
|
now_micros: input.now_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobGetRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobGetInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobGetRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobListOpenRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobListOpenInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobListOpenRecordInput) -> Self {
|
|
Self {
|
|
project_id: input.project_id,
|
|
owner_user_id: input.owner_user_id,
|
|
limit: input.limit,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobClaimRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobClaimInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobClaimRecordInput) -> Self {
|
|
Self {
|
|
worker_id: input.worker_id,
|
|
limit: input.limit,
|
|
lease_expires_at_micros: input.lease_expires_at_micros,
|
|
claimed_at_micros: input.claimed_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobRenewLeaseRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobRenewLeaseInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobRenewLeaseRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
worker_id: input.worker_id,
|
|
lease_token: input.lease_token,
|
|
lease_expires_at_micros: input.lease_expires_at_micros,
|
|
renewed_at_micros: input.renewed_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobCompleteRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobCompleteInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobCompleteRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
worker_id: input.worker_id,
|
|
lease_token: input.lease_token,
|
|
artifact_id: input.artifact_id,
|
|
preview_build_id: input.preview_build_id,
|
|
completed_at_micros: input.completed_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobCompletePreviewBuildRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobCompletePreviewBuildInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobCompletePreviewBuildRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
worker_id: input.worker_id,
|
|
lease_token: input.lease_token,
|
|
preview_build_id: input.preview_build_id,
|
|
artifact_id: input.artifact_id,
|
|
preview_token_id: input.preview_token_id,
|
|
preview_url: input.preview_url,
|
|
logs: input.logs,
|
|
started_at_micros: input.started_at_micros,
|
|
finished_at_micros: input.finished_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobFailRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobFailInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobFailRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
worker_id: input.worker_id,
|
|
lease_token: input.lease_token,
|
|
error_summary: input.error_summary,
|
|
failed_at_micros: input.failed_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobCancelRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobCancelInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobCancelRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
cancelled_at_micros: input.cancelled_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobStaleRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobStaleInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobStaleRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
stale_reason: input.stale_reason,
|
|
stale_at_micros: input.stale_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobExpireRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobExpireInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobExpireRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
expired_at_micros: input.expired_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobAppendLogRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobAppendLogInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobAppendLogRecordInput) -> Self {
|
|
Self {
|
|
log_id: input.log_id,
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
sequence: input.sequence,
|
|
level: input.level,
|
|
message: input.message,
|
|
worker_id: input.worker_id,
|
|
lease_token: input.lease_token,
|
|
created_at_micros: input.created_at_micros,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WebProjectRuntimeJobListLogsRecordInput>
|
|
for crate::module_bindings::WebProjectRuntimeJobListLogsInput
|
|
{
|
|
fn from(input: WebProjectRuntimeJobListLogsRecordInput) -> Self {
|
|
Self {
|
|
job_id: input.job_id,
|
|
owner_user_id: input.owner_user_id,
|
|
after_sequence: input.after_sequence,
|
|
limit: input.limit,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn map_web_project_procedure_result(
|
|
result: WebProjectProcedureResult,
|
|
) -> Result<WebProjectRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
result
|
|
.project
|
|
.map(map_web_project_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程"))
|
|
}
|
|
|
|
pub(crate) fn map_web_project_snapshot_procedure_result(
|
|
result: WebProjectSnapshotProcedureResult,
|
|
) -> Result<WebProjectSnapshotMutationRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
let project = result
|
|
.project
|
|
.map(map_web_project_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程"))?;
|
|
let snapshot = result
|
|
.snapshot
|
|
.map(map_web_project_source_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程快照"))?;
|
|
|
|
Ok(WebProjectSnapshotMutationRecord { project, snapshot })
|
|
}
|
|
|
|
pub(crate) fn map_web_project_preview_build_procedure_result(
|
|
result: WebProjectPreviewBuildProcedureResult,
|
|
) -> Result<WebProjectPreviewBuildMutationRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
let project = result
|
|
.project
|
|
.map(map_web_project_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程"))?;
|
|
let build = result
|
|
.build
|
|
.map(map_web_project_preview_build_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程预览构建"))?;
|
|
|
|
Ok(WebProjectPreviewBuildMutationRecord { project, build })
|
|
}
|
|
|
|
pub(crate) fn map_web_project_runtime_job_procedure_result(
|
|
result: WebProjectRuntimeJobProcedureResult,
|
|
) -> Result<WebProjectRuntimeJobRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
result
|
|
.job
|
|
.map(map_web_project_runtime_job_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web Project runtime job"))
|
|
}
|
|
|
|
pub(crate) fn map_web_project_runtime_job_preview_build_procedure_result(
|
|
result: WebProjectRuntimeJobPreviewBuildProcedureResult,
|
|
) -> Result<WebProjectRuntimeJobPreviewBuildMutationRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
let project = result
|
|
.project
|
|
.map(map_web_project_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程"))?;
|
|
let build = result
|
|
.build
|
|
.map(map_web_project_preview_build_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web 工程预览构建"))?;
|
|
let job = result
|
|
.job
|
|
.map(map_web_project_runtime_job_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web Project runtime job"))?;
|
|
|
|
Ok(WebProjectRuntimeJobPreviewBuildMutationRecord {
|
|
project,
|
|
build,
|
|
job,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn map_web_project_runtime_job_list_result(
|
|
result: WebProjectRuntimeJobProcedureResult,
|
|
) -> Result<Vec<WebProjectRuntimeJobRecord>, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
Ok(result
|
|
.jobs
|
|
.into_iter()
|
|
.map(map_web_project_runtime_job_snapshot)
|
|
.collect())
|
|
}
|
|
|
|
pub(crate) fn map_web_project_runtime_job_log_procedure_result(
|
|
result: WebProjectRuntimeJobProcedureResult,
|
|
) -> Result<WebProjectRuntimeJobLogRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
result
|
|
.log
|
|
.map(map_web_project_runtime_job_log_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("Web Project runtime job 日志"))
|
|
}
|
|
|
|
pub(crate) fn map_web_project_runtime_job_log_list_result(
|
|
result: WebProjectRuntimeJobProcedureResult,
|
|
) -> Result<Vec<WebProjectRuntimeJobLogRecord>, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
|
|
Ok(result
|
|
.logs
|
|
.into_iter()
|
|
.map(map_web_project_runtime_job_log_snapshot)
|
|
.collect())
|
|
}
|
|
|
|
fn map_web_project_snapshot(snapshot: WebProjectProjectSnapshot) -> WebProjectRecord {
|
|
WebProjectRecord {
|
|
project_id: snapshot.project_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
title: snapshot.title,
|
|
template_key: snapshot.template_key,
|
|
active_snapshot_id: snapshot.active_snapshot_id,
|
|
active_preview_build_id: snapshot.active_preview_build_id,
|
|
created_at: format_timestamp_micros(snapshot.created_at_micros),
|
|
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
|
|
}
|
|
}
|
|
|
|
fn map_web_project_source_snapshot(snapshot: WebProjectSnapshot) -> WebProjectSnapshotRecord {
|
|
WebProjectSnapshotRecord {
|
|
snapshot_id: snapshot.snapshot_id,
|
|
project_id: snapshot.project_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
parent_snapshot_id: snapshot.parent_snapshot_id,
|
|
template_key: snapshot.template_key,
|
|
files: snapshot.files.into_iter().map(Into::into).collect(),
|
|
patch_summary: snapshot.patch_summary,
|
|
created_by: snapshot.created_by,
|
|
created_at: format_timestamp_micros(snapshot.created_at_micros),
|
|
}
|
|
}
|
|
|
|
fn map_web_project_preview_build_snapshot(
|
|
snapshot: WebProjectPreviewBuildSnapshot,
|
|
) -> WebProjectPreviewBuildRecord {
|
|
WebProjectPreviewBuildRecord {
|
|
job_id: snapshot.job_id,
|
|
project_id: snapshot.project_id,
|
|
snapshot_id: snapshot.snapshot_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
status: snapshot.status,
|
|
logs: snapshot.logs,
|
|
artifact_id: snapshot.artifact_id,
|
|
preview_token_id: snapshot.preview_token_id,
|
|
preview_url: snapshot.preview_url,
|
|
error_summary: snapshot.error_summary,
|
|
created_at: format_timestamp_micros(snapshot.created_at_micros),
|
|
started_at: snapshot.started_at_micros.map(format_timestamp_micros),
|
|
finished_at: snapshot.finished_at_micros.map(format_timestamp_micros),
|
|
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
|
|
}
|
|
}
|
|
|
|
fn map_web_project_runtime_job_snapshot(
|
|
snapshot: WebProjectRuntimeJobSnapshot,
|
|
) -> WebProjectRuntimeJobRecord {
|
|
WebProjectRuntimeJobRecord {
|
|
job_id: snapshot.job_id,
|
|
project_id: snapshot.project_id,
|
|
snapshot_id: snapshot.snapshot_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
job_kind: snapshot.job_kind,
|
|
status: snapshot.status,
|
|
attempt: snapshot.attempt,
|
|
worker_id: snapshot.worker_id,
|
|
lease_token: snapshot.lease_token,
|
|
lease_expires_at: snapshot
|
|
.lease_expires_at_micros
|
|
.map(format_timestamp_micros),
|
|
cancel_requested_at: snapshot
|
|
.cancel_requested_at_micros
|
|
.map(format_timestamp_micros),
|
|
stale_reason: snapshot.stale_reason,
|
|
artifact_id: snapshot.artifact_id,
|
|
preview_build_id: snapshot.preview_build_id,
|
|
error_summary: snapshot.error_summary,
|
|
created_at: format_timestamp_micros(snapshot.created_at_micros),
|
|
started_at: snapshot.started_at_micros.map(format_timestamp_micros),
|
|
finished_at: snapshot.finished_at_micros.map(format_timestamp_micros),
|
|
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
|
|
}
|
|
}
|
|
|
|
fn map_web_project_runtime_job_log_snapshot(
|
|
snapshot: WebProjectRuntimeJobLogSnapshot,
|
|
) -> WebProjectRuntimeJobLogRecord {
|
|
WebProjectRuntimeJobLogRecord {
|
|
log_id: snapshot.log_id,
|
|
job_id: snapshot.job_id,
|
|
project_id: snapshot.project_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
sequence: snapshot.sequence,
|
|
level: snapshot.level,
|
|
message: snapshot.message,
|
|
created_at: format_timestamp_micros(snapshot.created_at_micros),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn maps_web_project_procedure_error() {
|
|
let result = WebProjectProcedureResult {
|
|
ok: false,
|
|
project: None,
|
|
error_message: Some("无权访问该 Web 工程".to_string()),
|
|
};
|
|
|
|
assert_eq!(
|
|
map_web_project_procedure_result(result)
|
|
.expect_err("procedure error should map")
|
|
.to_string(),
|
|
"无权访问该 Web 工程"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn maps_runtime_job_log_list_in_sequence_order_from_module_result() {
|
|
let result = WebProjectRuntimeJobProcedureResult {
|
|
ok: true,
|
|
job: None,
|
|
jobs: Vec::new(),
|
|
log: None,
|
|
logs: vec![WebProjectRuntimeJobLogSnapshot {
|
|
log_id: "log-1".to_string(),
|
|
job_id: "job-1".to_string(),
|
|
project_id: "project-1".to_string(),
|
|
owner_user_id: "user-1".to_string(),
|
|
sequence: 2,
|
|
level: "info".to_string(),
|
|
message: "构建完成".to_string(),
|
|
created_at_micros: 1_700_000_000_000_000,
|
|
}],
|
|
error_message: None,
|
|
};
|
|
|
|
let logs = map_web_project_runtime_job_log_list_result(result)
|
|
.expect("runtime job logs should map");
|
|
|
|
assert_eq!(logs.len(), 1);
|
|
assert_eq!(logs[0].sequence, 2);
|
|
assert_eq!(logs[0].message, "构建完成");
|
|
}
|
|
|
|
#[test]
|
|
fn maps_runtime_job_preview_build_combo_result() {
|
|
let result = WebProjectRuntimeJobPreviewBuildProcedureResult {
|
|
ok: true,
|
|
project: Some(WebProjectProjectSnapshot {
|
|
project_id: "project-1".to_string(),
|
|
owner_user_id: "user-1".to_string(),
|
|
title: "测试工程".to_string(),
|
|
template_key: "react-vite-ts-static".to_string(),
|
|
active_snapshot_id: "snapshot-2".to_string(),
|
|
active_preview_build_id: Some("build-2".to_string()),
|
|
created_at_micros: 1_700_000_000_000_000,
|
|
updated_at_micros: 1_700_000_010_000_000,
|
|
}),
|
|
build: Some(WebProjectPreviewBuildSnapshot {
|
|
job_id: "build-1".to_string(),
|
|
project_id: "project-1".to_string(),
|
|
snapshot_id: "snapshot-1".to_string(),
|
|
owner_user_id: "user-1".to_string(),
|
|
status: "stale".to_string(),
|
|
logs: vec!["runtime worker: 构建完成,预览地址已生成".to_string()],
|
|
artifact_id: None,
|
|
preview_token_id: None,
|
|
preview_url: None,
|
|
error_summary: Some("job snapshot 已不是项目 active snapshot".to_string()),
|
|
created_at_micros: 1_700_000_000_000_000,
|
|
started_at_micros: Some(1_700_000_005_000_000),
|
|
finished_at_micros: Some(1_700_000_010_000_000),
|
|
updated_at_micros: 1_700_000_010_000_000,
|
|
}),
|
|
job: Some(WebProjectRuntimeJobSnapshot {
|
|
job_id: "job-1".to_string(),
|
|
project_id: "project-1".to_string(),
|
|
snapshot_id: "snapshot-1".to_string(),
|
|
owner_user_id: "user-1".to_string(),
|
|
job_kind: "preview_build".to_string(),
|
|
status: "stale".to_string(),
|
|
attempt: 1,
|
|
worker_id: None,
|
|
lease_token: None,
|
|
lease_expires_at_micros: None,
|
|
cancel_requested_at_micros: None,
|
|
stale_reason: Some("job snapshot 已不是项目 active snapshot".to_string()),
|
|
artifact_id: None,
|
|
preview_build_id: Some("build-1".to_string()),
|
|
error_summary: None,
|
|
created_at_micros: 1_700_000_000_000_000,
|
|
started_at_micros: Some(1_700_000_005_000_000),
|
|
finished_at_micros: Some(1_700_000_010_000_000),
|
|
updated_at_micros: 1_700_000_010_000_000,
|
|
}),
|
|
error_message: None,
|
|
};
|
|
|
|
let mapped = map_web_project_runtime_job_preview_build_procedure_result(result)
|
|
.expect("combo result should map");
|
|
|
|
assert_eq!(
|
|
mapped.project.active_preview_build_id.as_deref(),
|
|
Some("build-2")
|
|
);
|
|
assert_eq!(mapped.build.status, "stale");
|
|
assert_eq!(mapped.build.preview_url, None);
|
|
assert_eq!(mapped.job.status, "stale");
|
|
assert_eq!(
|
|
mapped.job.stale_reason.as_deref(),
|
|
Some("job snapshot 已不是项目 active snapshot")
|
|
);
|
|
}
|
|
}
|