Skip to content

Commit da7c660

Browse files
committed
feat(luminork): Add ability to see if a schema is upgradable
1 parent d57a38e commit da7c660

File tree

6 files changed

+81
-0
lines changed

6 files changed

+81
-0
lines changed

lib/luminork-server/src/service/v1/schemas/create_schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub async fn create_schema(
9393
default_variant_id: created_schema_variant.id,
9494
variant_ids: variants.into_iter().map(|v| v.id).collect(),
9595
schema_id: schema.id(),
96+
upgrade_available: None, // Newly created schema, not from a module
9697
}))
9798
}
9899

lib/luminork-server/src/service/v1/schemas/find_schema.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ pub async fn find_schema(
173173
},
174174
};
175175

176+
// Check if an upgrade is available (only applicable if schema is installed)
177+
let upgrade_available = if installed {
178+
super::check_schema_upgrade_available(ctx, schema_id).await?
179+
} else {
180+
None
181+
};
182+
176183
tracker.track(
177184
ctx,
178185
"api_find_schema",
@@ -186,6 +193,7 @@ pub async fn find_schema(
186193
schema_id,
187194
category,
188195
installed,
196+
upgrade_available,
189197
}))
190198
}
191199

@@ -200,6 +208,8 @@ pub struct FindSchemaV1Response {
200208
pub category: Option<String>,
201209
#[schema(value_type = bool)]
202210
pub installed: bool,
211+
#[schema(value_type = Option<bool>, example = true)]
212+
pub upgrade_available: Option<bool>,
203213
}
204214

205215
enum SchemaReference {

lib/luminork-server/src/service/v1/schemas/get_schema.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ pub async fn get_schema(
5959
let default_variant_id = Schema::default_variant_id(ctx, schema_id).await?;
6060
let variants = SchemaVariant::list_for_schema(ctx, schema_id).await?;
6161

62+
// Check if an upgrade is available
63+
let upgrade_available = super::check_schema_upgrade_available(ctx, schema_id).await?;
64+
6265
tracker.track(
6366
ctx,
6467
"api_get_schema",
@@ -75,6 +78,7 @@ pub async fn get_schema(
7578
name: schema.name,
7679
default_variant_id,
7780
variant_ids: variants.into_iter().map(|v| v.id).collect_vec(),
81+
upgrade_available,
7882
}));
7983
}
8084

@@ -101,6 +105,7 @@ pub async fn get_schema(
101105
name: cached_schema.name,
102106
default_variant_id: cached_schema.default_variant_id,
103107
variant_ids: cached_schema.variant_ids,
108+
upgrade_available: None, // Not installed, so no upgrade check possible
104109
}));
105110
}
106111
}

lib/luminork-server/src/service/v1/schemas/install_schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,6 @@ pub async fn install_schema(
7676
name: schema.name,
7777
default_variant_id,
7878
variant_ids: variants.into_iter().map(|v| v.id).collect_vec(),
79+
upgrade_available: Some(false), // Just installed, no upgrade available
7980
}))
8081
}

lib/luminork-server/src/service/v1/schemas/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@ pub struct GetSchemaV1Response {
781781
pub default_variant_id: SchemaVariantId,
782782
#[schema(value_type = Vec<String>, example = json!(["01H9ZQD35JPMBGHH69BT0Q79VZ", "01H9ZQD35JPMBGHH69BT0Q79VY"]))]
783783
pub variant_ids: Vec<SchemaVariantId>,
784+
#[schema(value_type = Option<bool>, example = true)]
785+
pub upgrade_available: Option<bool>,
784786
}
785787

786788
/// The response payload when materialized views or data is being built referenced by present or
@@ -921,6 +923,41 @@ pub struct SchemaResponse {
921923
pub schema_id: SchemaId,
922924
#[schema(value_type = bool, example = "false")]
923925
pub installed: bool,
926+
#[schema(value_type = Option<bool>, example = true)]
927+
pub upgrade_available: Option<bool>,
928+
}
929+
930+
/// Checks if an upgrade is available for an installed schema by comparing
931+
/// the installed module hash with the latest cached module hash.
932+
///
933+
/// Returns:
934+
/// - `None` if the schema is not installed (no comparison possible)
935+
/// - `Some(true)` if an upgrade is available (hashes differ)
936+
/// - `Some(false)` if no upgrade is available (hashes match or no cached module found)
937+
pub async fn check_schema_upgrade_available(
938+
ctx: &DalContext,
939+
schema_id: SchemaId,
940+
) -> SchemaResult<Option<bool>> {
941+
// Get the latest cached module to find what the latest available version is
942+
let Some(cached_module) = CachedModule::find_latest_for_schema_id(ctx, schema_id).await? else {
943+
// No cached module found, so we can't determine if an upgrade is available
944+
return Ok(Some(false));
945+
};
946+
947+
// Try to find the installed module using the schema_id as the module_schema_id
948+
// Convert SchemaId to Ulid
949+
let module_schema_id: si_events::ulid::Ulid = schema_id.into();
950+
let installed_module = dal::module::Module::find_for_module_schema_id(ctx, module_schema_id).await?;
951+
952+
let Some(installed_module) = installed_module else {
953+
// Schema is not installed, return None to indicate no comparison is possible
954+
return Ok(None);
955+
};
956+
957+
// Compare the installed module's root_hash with the cached module's latest_hash
958+
let upgrade_available = installed_module.root_hash() != cached_module.latest_hash;
959+
960+
Ok(Some(upgrade_available))
924961
}
925962

926963
pub async fn get_full_schema_list(ctx: &DalContext) -> SchemaResult<Vec<SchemaResponse>> {
@@ -941,21 +978,25 @@ pub async fn get_full_schema_list(ctx: &DalContext) -> SchemaResult<Vec<SchemaRe
941978
for schema_id in &schema_ids {
942979
if let Some(module) = cached_module_map.get(schema_id) {
943980
// Schema is both installed and in cache
981+
let upgrade_available = check_schema_upgrade_available(ctx, *schema_id).await?;
944982
all_schemas.push(SchemaResponse {
945983
schema_name: module.schema_name.clone(),
946984
schema_id: *schema_id,
947985
category: module.category.clone(),
948986
installed: true,
987+
upgrade_available,
949988
});
950989
} else {
951990
// Schema is installed but not in cache - this is a local only schema
952991
if let Ok(schema) = dal::Schema::get_by_id(ctx, *schema_id).await {
953992
let default_variant = SchemaVariant::default_for_schema(ctx, *schema_id).await?;
993+
let upgrade_available = check_schema_upgrade_available(ctx, *schema_id).await?;
954994
all_schemas.push(SchemaResponse {
955995
schema_name: schema.name,
956996
schema_id: *schema_id,
957997
category: Some(default_variant.category().to_owned()),
958998
installed: true,
999+
upgrade_available,
9591000
});
9601001
}
9611002
}
@@ -971,6 +1012,7 @@ pub async fn get_full_schema_list(ctx: &DalContext) -> SchemaResult<Vec<SchemaRe
9711012
schema_id,
9721013
category: module.category,
9731014
installed: is_installed,
1015+
upgrade_available: None, // Not installed, so no upgrade check possible
9741016
});
9751017
}
9761018

lib/luminork-server/src/service/v1/schemas/search_schemas.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,16 @@ pub async fn search_schemas(
5050
all_schemas = apply_category_filter(all_schemas, category).await?;
5151
}
5252

53+
if payload.upgradable_only == Some(true) {
54+
all_schemas = apply_upgradable_filter(all_schemas).await?;
55+
}
56+
5357
tracker.track(
5458
ctx,
5559
"api_search_schemas",
5660
json!({
5761
"category": payload.category,
62+
"upgradable_only": payload.upgradable_only,
5863
}),
5964
);
6065

@@ -78,11 +83,28 @@ async fn apply_category_filter(
7883
Ok(filtered_schemas)
7984
}
8085

86+
async fn apply_upgradable_filter(
87+
schemas: Vec<SchemaResponse>,
88+
) -> SchemaResult<Vec<SchemaResponse>> {
89+
let mut filtered_schemas = Vec::new();
90+
91+
for schema in schemas {
92+
// Only include schemas that are installed and have an upgrade available
93+
if schema.installed && schema.upgrade_available == Some(true) {
94+
filtered_schemas.push(schema);
95+
}
96+
}
97+
98+
Ok(filtered_schemas)
99+
}
100+
81101
#[derive(Deserialize, Serialize, Debug, ToSchema)]
82102
#[serde(rename_all = "camelCase")]
83103
pub struct SearchSchemasV1Request {
84104
#[schema(example = "AWS::EC2", required = false)]
85105
pub category: Option<String>,
106+
#[schema(example = true, required = false)]
107+
pub upgradable_only: Option<bool>,
86108
}
87109

88110
#[derive(Deserialize, Serialize, Debug, ToSchema)]

0 commit comments

Comments
 (0)