Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub async fn create_schema(
default_variant_id: created_schema_variant.id,
variant_ids: variants.into_iter().map(|v| v.id).collect(),
schema_id: schema.id(),
upgrade_available: None, // Newly created schema, not from a module
}))
}

Expand Down
10 changes: 10 additions & 0 deletions lib/luminork-server/src/service/v1/schemas/find_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ pub async fn find_schema(
},
};

// Check if an upgrade is available (only applicable if schema is installed)
let upgrade_available = if installed {
super::check_schema_upgrade_available(ctx, schema_id).await?
} else {
None
};

tracker.track(
ctx,
"api_find_schema",
Expand All @@ -186,6 +193,7 @@ pub async fn find_schema(
schema_id,
category,
installed,
upgrade_available,
}))
}

Expand All @@ -200,6 +208,8 @@ pub struct FindSchemaV1Response {
pub category: Option<String>,
#[schema(value_type = bool)]
pub installed: bool,
#[schema(value_type = Option<bool>, example = true)]
pub upgrade_available: Option<bool>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just a bool?

}

enum SchemaReference {
Expand Down
5 changes: 5 additions & 0 deletions lib/luminork-server/src/service/v1/schemas/get_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ pub async fn get_schema(
let default_variant_id = Schema::default_variant_id(ctx, schema_id).await?;
let variants = SchemaVariant::list_for_schema(ctx, schema_id).await?;

// Check if an upgrade is available
let upgrade_available = super::check_schema_upgrade_available(ctx, schema_id).await?;

tracker.track(
ctx,
"api_get_schema",
Expand All @@ -75,6 +78,7 @@ pub async fn get_schema(
name: schema.name,
default_variant_id,
variant_ids: variants.into_iter().map(|v| v.id).collect_vec(),
upgrade_available,
}));
}

Expand All @@ -101,6 +105,7 @@ pub async fn get_schema(
name: cached_schema.name,
default_variant_id: cached_schema.default_variant_id,
variant_ids: cached_schema.variant_ids,
upgrade_available: None, // Not installed, so no upgrade check possible
}));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,6 @@ pub async fn install_schema(
name: schema.name,
default_variant_id,
variant_ids: variants.into_iter().map(|v| v.id).collect_vec(),
upgrade_available: Some(false), // Just installed, no upgrade available
}))
}
42 changes: 42 additions & 0 deletions lib/luminork-server/src/service/v1/schemas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,8 @@ pub struct GetSchemaV1Response {
pub default_variant_id: SchemaVariantId,
#[schema(value_type = Vec<String>, example = json!(["01H9ZQD35JPMBGHH69BT0Q79VZ", "01H9ZQD35JPMBGHH69BT0Q79VY"]))]
pub variant_ids: Vec<SchemaVariantId>,
#[schema(value_type = Option<bool>, example = true)]
pub upgrade_available: Option<bool>,
}

/// The response payload when materialized views or data is being built referenced by present or
Expand Down Expand Up @@ -921,6 +923,41 @@ pub struct SchemaResponse {
pub schema_id: SchemaId,
#[schema(value_type = bool, example = "false")]
pub installed: bool,
#[schema(value_type = Option<bool>, example = true)]
pub upgrade_available: Option<bool>,
}

/// Checks if an upgrade is available for an installed schema by comparing
/// the installed module hash with the latest cached module hash.
///
/// Returns:
/// - `None` if the schema is not installed (no comparison possible)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Part of me wonders if this will be clear in the response via the API to the user.

/// - `Some(true)` if an upgrade is available (hashes differ)
/// - `Some(false)` if no upgrade is available (hashes match or no cached module found)
pub async fn check_schema_upgrade_available(
ctx: &DalContext,
schema_id: SchemaId,
) -> SchemaResult<Option<bool>> {
// Get the latest cached module to find what the latest available version is
let Some(cached_module) = CachedModule::find_latest_for_schema_id(ctx, schema_id).await? else {
// No cached module found, so we can't determine if an upgrade is available
return Ok(Some(false));
};

// Try to find the installed module using the schema_id as the module_schema_id
// Convert SchemaId to Ulid
let module_schema_id: si_events::ulid::Ulid = schema_id.into();
let installed_module = dal::module::Module::find_for_module_schema_id(ctx, module_schema_id).await?;

let Some(installed_module) = installed_module else {
// Schema is not installed, return None to indicate no comparison is possible
return Ok(None);
};

// Compare the installed module's root_hash with the cached module's latest_hash
let upgrade_available = installed_module.root_hash() != cached_module.latest_hash;

Ok(Some(upgrade_available))
}

pub async fn get_full_schema_list(ctx: &DalContext) -> SchemaResult<Vec<SchemaResponse>> {
Expand All @@ -941,21 +978,25 @@ pub async fn get_full_schema_list(ctx: &DalContext) -> SchemaResult<Vec<SchemaRe
for schema_id in &schema_ids {
if let Some(module) = cached_module_map.get(schema_id) {
// Schema is both installed and in cache
let upgrade_available = check_schema_upgrade_available(ctx, *schema_id).await?;
all_schemas.push(SchemaResponse {
schema_name: module.schema_name.clone(),
schema_id: *schema_id,
category: module.category.clone(),
installed: true,
upgrade_available,
});
} else {
// Schema is installed but not in cache - this is a local only schema
if let Ok(schema) = dal::Schema::get_by_id(ctx, *schema_id).await {
let default_variant = SchemaVariant::default_for_schema(ctx, *schema_id).await?;
let upgrade_available = check_schema_upgrade_available(ctx, *schema_id).await?;
all_schemas.push(SchemaResponse {
schema_name: schema.name,
schema_id: *schema_id,
category: Some(default_variant.category().to_owned()),
installed: true,
upgrade_available,
});
}
}
Expand All @@ -971,6 +1012,7 @@ pub async fn get_full_schema_list(ctx: &DalContext) -> SchemaResult<Vec<SchemaRe
schema_id,
category: module.category,
installed: is_installed,
upgrade_available: None, // Not installed, so no upgrade check possible
});
}

Expand Down
22 changes: 22 additions & 0 deletions lib/luminork-server/src/service/v1/schemas/search_schemas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,16 @@ pub async fn search_schemas(
all_schemas = apply_category_filter(all_schemas, category).await?;
}

if payload.upgradable_only == Some(true) {
all_schemas = apply_upgradable_filter(all_schemas).await?;
}

tracker.track(
ctx,
"api_search_schemas",
json!({
"category": payload.category,
"upgradable_only": payload.upgradable_only,
}),
);

Expand All @@ -78,11 +83,28 @@ async fn apply_category_filter(
Ok(filtered_schemas)
}

async fn apply_upgradable_filter(
schemas: Vec<SchemaResponse>,
) -> SchemaResult<Vec<SchemaResponse>> {
let mut filtered_schemas = Vec::new();

for schema in schemas {
// Only include schemas that are installed and have an upgrade available
if schema.installed && schema.upgrade_available == Some(true) {
filtered_schemas.push(schema);
}
}

Ok(filtered_schemas)
}

#[derive(Deserialize, Serialize, Debug, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchSchemasV1Request {
#[schema(example = "AWS::EC2", required = false)]
pub category: Option<String>,
#[schema(example = true, required = false)]
pub upgradable_only: Option<bool>,
}

#[derive(Deserialize, Serialize, Debug, ToSchema)]
Expand Down
Loading