feat: centralize workspace profile DTOs
This commit is contained in:
@@ -12,11 +12,15 @@ use worker_runtime::config_bundle::{
|
||||
ConfigBundle, ConfigBundleMetadata, ConfigBundleProvenance, ConfigProfileDescriptor,
|
||||
};
|
||||
use worker_runtime::profile_archive::{ProfileSourceArchive, ProfileSourceArchiveInput};
|
||||
use workspace_api::{
|
||||
Diagnostic, DiagnosticSeverity, ProfileSettingsResponse, UpdateWorkspaceMetadataRequest,
|
||||
WorkspaceMetadataSettingsResponse, WorkspaceProfileSourceProvenance,
|
||||
WorkspaceProfileSourceSummary, WorkspaceProfileSummary,
|
||||
};
|
||||
|
||||
use crate::config_source::{
|
||||
WorkspaceConfigSchemaProvider, WorkspaceConfigState, evaluate_workspace_config_state,
|
||||
};
|
||||
use crate::hosts::{DiagnosticSeverity, RuntimeDiagnostic};
|
||||
use crate::{Error, Result};
|
||||
|
||||
const PROFILE_SCHEMA_SOURCE: &str = r#"{
|
||||
@@ -427,81 +431,6 @@ fn build_virtual_profile_archive(
|
||||
.map_err(|error| profile_validation_error("profile_source_archive_invalid", &error.to_string()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkspaceMetadataSettingsResponse {
|
||||
pub workspace_id: String,
|
||||
pub display_name: String,
|
||||
pub created_at: String,
|
||||
pub revision: String,
|
||||
pub source: String,
|
||||
pub diagnostics: Vec<RuntimeDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct UpdateWorkspaceMetadataRequest {
|
||||
pub display_name: String,
|
||||
pub revision: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkspaceMetadataMutationResponse {
|
||||
pub workspace: WorkspaceMetadataSettingsResponse,
|
||||
pub diagnostics: Vec<RuntimeDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProfileSettingsResponse {
|
||||
pub workspace_id: String,
|
||||
pub registry_revision: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub config_revision: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub tree_digest: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub projection_digest: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_profile: Option<String>,
|
||||
pub profiles: Vec<WorkspaceProfileSummary>,
|
||||
pub sources: Vec<WorkspaceProfileSourceSummary>,
|
||||
pub diagnostics: Vec<RuntimeDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkspaceProfileSummary {
|
||||
pub profile_id: String,
|
||||
pub selector: String,
|
||||
pub label: String,
|
||||
pub source_kind: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub profile_source_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub editable: bool,
|
||||
pub is_default: bool,
|
||||
pub diagnostics: Vec<RuntimeDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkspaceProfileSourceSummary {
|
||||
pub profile_source_id: String,
|
||||
pub display_path: String,
|
||||
pub kind: String,
|
||||
pub content_type: String,
|
||||
pub content_digest: String,
|
||||
pub provenance: WorkspaceProfileSourceProvenance,
|
||||
pub editable: bool,
|
||||
pub revision: String,
|
||||
pub size_bytes: u64,
|
||||
pub diagnostics: Vec<RuntimeDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WorkspaceProfileSourceProvenance {
|
||||
ProjectProfileSourceTree,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct WorkspaceIdentityFile {
|
||||
@@ -804,8 +733,8 @@ fn diagnostic(
|
||||
code: impl Into<String>,
|
||||
severity: DiagnosticSeverity,
|
||||
message: impl Into<String>,
|
||||
) -> RuntimeDiagnostic {
|
||||
RuntimeDiagnostic {
|
||||
) -> Diagnostic {
|
||||
Diagnostic {
|
||||
code: code.into(),
|
||||
severity,
|
||||
message: message.into(),
|
||||
|
||||
@@ -61,20 +61,22 @@ use workspace_api::{
|
||||
CreateWorkspaceRepositoryRequest, CreateWorkspaceRepositoryResponse,
|
||||
DeleteRepositorySshCredentialRequest, DeleteRepositorySshHostTrustRequest,
|
||||
ObjectiveCreateRequest, ObjectiveEditRequest, ObjectiveLinkTicketRequest,
|
||||
ObjectiveStateRequest, PutRepositorySshHostTrustRequest, RepositoryAccessProjection,
|
||||
RepositoryDetailResponse, RepositoryListResponse, RepositoryLogResponse,
|
||||
RepositorySshCredential, RepositorySshHostTrust, RotateRepositorySshCredentialRequest,
|
||||
RuntimeConnectionTestResponse, RuntimeManagementSummary, TICKET_ORCHESTRATION_PLANS_QUERY_PATH,
|
||||
TICKET_RELATIONS_QUERY_PATH,
|
||||
ObjectiveStateRequest, ProfileSettingsResponse, PutRepositorySshHostTrustRequest,
|
||||
RepositoryAccessProjection, RepositoryDetailResponse, RepositoryListResponse,
|
||||
RepositoryLogResponse, RepositorySshCredential, RepositorySshHostTrust,
|
||||
RotateRepositorySshCredentialRequest, RuntimeConnectionTestResponse, RuntimeManagementSummary,
|
||||
TICKET_ORCHESTRATION_PLANS_QUERY_PATH, TICKET_RELATIONS_QUERY_PATH,
|
||||
UpdateWorkspaceMetadataRequest,
|
||||
WorkingDirectoryCreateRequest as BrowserWorkingDirectoryCreateRequest,
|
||||
WorkingDirectoryCreateResponse as BrowserWorkingDirectoryCreateResponse,
|
||||
WorkingDirectoryDetailResponse as BrowserWorkingDirectoryDetailResponse,
|
||||
WorkingDirectoryListResponse as BrowserWorkingDirectoryListResponse,
|
||||
WorkingDirectoryRemovalDisposition, WorkingDirectoryRemovalRequest,
|
||||
WorkingDirectoryRemovalResponse, WorkspaceCatalogListResponse, WorkspaceCreateResponse,
|
||||
WorkspaceExtensionPointState, WorkspaceExtensionPoints, WorkspacePermissionSummary,
|
||||
WorkspaceRepositoryRecord, WorkspaceResponse, WorkspaceRuntimeResource, WorkspaceSummary,
|
||||
WorkspaceWorkerDiscoveryItem, WorkspaceWorkerDiscoveryPage, WorkspaceWorkerSubject,
|
||||
WorkspaceExtensionPointState, WorkspaceExtensionPoints, WorkspaceMetadataMutationResponse,
|
||||
WorkspaceMetadataSettingsResponse, WorkspacePermissionSummary, WorkspaceRepositoryRecord,
|
||||
WorkspaceResponse, WorkspaceRuntimeResource, WorkspaceSummary, WorkspaceWorkerDiscoveryItem,
|
||||
WorkspaceWorkerDiscoveryPage, WorkspaceWorkerSubject,
|
||||
};
|
||||
|
||||
use crate::auth::{
|
||||
@@ -114,7 +116,6 @@ use crate::observation::{
|
||||
BackendObservationProxy, ObservationProxyError, RuntimeObservationClient,
|
||||
RuntimeObservationSource, RuntimeObservationSourceConfig,
|
||||
};
|
||||
use crate::profile_settings::UpdateWorkspaceMetadataRequest;
|
||||
use crate::records::{
|
||||
MergeRequestListItem, MergeRequestListResponse, ObjectiveDetail, ObjectiveQueryRequest,
|
||||
ObjectiveQueryResponse, ObjectiveShowRequest, ProjectRecordList, TicketDetail,
|
||||
@@ -3360,7 +3361,7 @@ async fn scoped_get_workspace(
|
||||
async fn scoped_get_workspace_settings(
|
||||
State(api): State<WorkspaceApi>,
|
||||
AxumPath(path): AxumPath<ScopedWorkspacePath>,
|
||||
) -> ApiResult<Json<crate::profile_settings::WorkspaceMetadataSettingsResponse>> {
|
||||
) -> ApiResult<Json<WorkspaceMetadataSettingsResponse>> {
|
||||
validate_workspace_scope(&api, &path.workspace_id)?;
|
||||
Ok(Json(crate::profile_settings::workspace_metadata_settings(
|
||||
&api.config.workspace_root,
|
||||
@@ -3374,20 +3375,18 @@ async fn scoped_update_workspace_settings(
|
||||
State(api): State<WorkspaceApi>,
|
||||
AxumPath(path): AxumPath<ScopedWorkspacePath>,
|
||||
Json(request): Json<UpdateWorkspaceMetadataRequest>,
|
||||
) -> ApiResult<Json<crate::profile_settings::WorkspaceMetadataMutationResponse>> {
|
||||
) -> ApiResult<Json<WorkspaceMetadataMutationResponse>> {
|
||||
validate_workspace_scope(&api, &path.workspace_id)?;
|
||||
let workspace =
|
||||
crate::profile_settings::update_workspace_metadata(&api.config.workspace_root, request)?;
|
||||
Ok(Json(
|
||||
crate::profile_settings::WorkspaceMetadataMutationResponse {
|
||||
workspace,
|
||||
diagnostics: vec![RuntimeDiagnostic {
|
||||
code: "workspace_metadata_updated".to_string(),
|
||||
severity: DiagnosticSeverity::Info,
|
||||
message: "Workspace display metadata was updated.".to_string(),
|
||||
}],
|
||||
},
|
||||
))
|
||||
Ok(Json(WorkspaceMetadataMutationResponse {
|
||||
workspace,
|
||||
diagnostics: vec![workspace_api::Diagnostic {
|
||||
code: "workspace_metadata_updated".to_string(),
|
||||
severity: workspace_api::DiagnosticSeverity::Info,
|
||||
message: "Workspace display metadata was updated.".to_string(),
|
||||
}],
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -3763,7 +3762,7 @@ async fn scoped_commit_workspace_config_tree(
|
||||
async fn scoped_get_profile_settings(
|
||||
State(api): State<WorkspaceApi>,
|
||||
AxumPath(path): AxumPath<ScopedWorkspacePath>,
|
||||
) -> ApiResult<Json<crate::profile_settings::ProfileSettingsResponse>> {
|
||||
) -> ApiResult<Json<ProfileSettingsResponse>> {
|
||||
validate_workspace_scope(&api, &path.workspace_id)?;
|
||||
let state = api
|
||||
.config_store
|
||||
@@ -14353,7 +14352,7 @@ fn worker_launch_options_response(api: &WorkspaceApi) -> ApiResult<WorkerLaunchO
|
||||
!profile
|
||||
.diagnostics
|
||||
.iter()
|
||||
.any(|diagnostic| diagnostic.severity == DiagnosticSeverity::Error)
|
||||
.any(|diagnostic| diagnostic.severity == workspace_api::DiagnosticSeverity::Error)
|
||||
})
|
||||
.map(|profile| WorkerLaunchProfileCandidate {
|
||||
id: profile.profile_id,
|
||||
@@ -26640,6 +26639,13 @@ VALUES ('0192f0e8-4d84-7d6e-a000-000000000001', 'ticket', 3);
|
||||
#[tokio::test]
|
||||
async fn profile_settings_are_read_only_virtual_config_projection() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let legacy_profile_dir = dir.path().join(".yoi/profiles");
|
||||
std::fs::create_dir_all(&legacy_profile_dir).unwrap();
|
||||
std::fs::write(
|
||||
legacy_profile_dir.join("legacy.dcdl"),
|
||||
"LegacyFilesystemProfile = { label = 'must not be projected' }\n",
|
||||
)
|
||||
.unwrap();
|
||||
let app = test_app(dir.path()).await;
|
||||
let path = format!("/api/w/{TEST_WORKSPACE_ID}/settings/profiles");
|
||||
let settings = get_json(app.clone(), &path).await;
|
||||
@@ -26654,6 +26660,8 @@ VALUES ('0192f0e8-4d84-7d6e-a000-000000000001', 'ticket', 3);
|
||||
.iter()
|
||||
.all(|profile| profile["editable"] == false)
|
||||
);
|
||||
assert!(!settings.to_string().contains("LegacyFilesystemProfile"));
|
||||
assert!(!settings.to_string().contains("legacy.dcdl"));
|
||||
let mutation = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
|
||||
Reference in New Issue
Block a user