artifact management
This commit is contained in:
@@ -8,6 +8,11 @@ use sqlx::{Executor, Postgres, QueryBuilder};
|
||||
|
||||
use super::{Create, Delete, FindById, FindByRef, List, Repository, Update};
|
||||
|
||||
/// Columns selected in all Action queries. Must match the `Action` model's `FromRow` fields.
|
||||
pub const ACTION_COLUMNS: &str = "id, ref, pack, pack_ref, label, description, entrypoint, \
|
||||
runtime, runtime_version_constraint, param_schema, out_schema, workflow_def, is_adhoc, \
|
||||
parameter_delivery, parameter_format, output_format, created, updated";
|
||||
|
||||
/// Filters for [`ActionRepository::list_search`].
|
||||
///
|
||||
/// All fields are optional and combinable (AND). Pagination is always applied.
|
||||
@@ -65,6 +70,9 @@ pub struct UpdateActionInput {
|
||||
pub runtime_version_constraint: Option<Option<String>>,
|
||||
pub param_schema: Option<JsonSchema>,
|
||||
pub out_schema: Option<JsonSchema>,
|
||||
pub parameter_delivery: Option<String>,
|
||||
pub parameter_format: Option<String>,
|
||||
pub output_format: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@@ -73,15 +81,10 @@ impl FindById for ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let action = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE id = $1
|
||||
"#,
|
||||
)
|
||||
let action = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE id = $1",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(id)
|
||||
.fetch_optional(executor)
|
||||
.await?;
|
||||
@@ -96,15 +99,10 @@ impl FindByRef for ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let action = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE ref = $1
|
||||
"#,
|
||||
)
|
||||
let action = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE ref = $1",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(ref_str)
|
||||
.fetch_optional(executor)
|
||||
.await?;
|
||||
@@ -119,15 +117,10 @@ impl List for ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let actions = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
ORDER BY ref ASC
|
||||
"#,
|
||||
)
|
||||
let actions = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action ORDER BY ref ASC",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
|
||||
@@ -155,16 +148,15 @@ impl Create for ActionRepository {
|
||||
}
|
||||
|
||||
// Try to insert - database will enforce uniqueness constraint
|
||||
let action = sqlx::query_as::<_, Action>(
|
||||
let action = sqlx::query_as::<_, Action>(&format!(
|
||||
r#"
|
||||
INSERT INTO action (ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint, param_schema, out_schema, is_adhoc)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
RETURNING id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
RETURNING {}
|
||||
"#,
|
||||
)
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(&input.r#ref)
|
||||
.bind(input.pack)
|
||||
.bind(&input.pack_ref)
|
||||
@@ -267,6 +259,33 @@ impl Update for ActionRepository {
|
||||
has_updates = true;
|
||||
}
|
||||
|
||||
if let Some(parameter_delivery) = &input.parameter_delivery {
|
||||
if has_updates {
|
||||
query.push(", ");
|
||||
}
|
||||
query.push("parameter_delivery = ");
|
||||
query.push_bind(parameter_delivery);
|
||||
has_updates = true;
|
||||
}
|
||||
|
||||
if let Some(parameter_format) = &input.parameter_format {
|
||||
if has_updates {
|
||||
query.push(", ");
|
||||
}
|
||||
query.push("parameter_format = ");
|
||||
query.push_bind(parameter_format);
|
||||
has_updates = true;
|
||||
}
|
||||
|
||||
if let Some(output_format) = &input.output_format {
|
||||
if has_updates {
|
||||
query.push(", ");
|
||||
}
|
||||
query.push("output_format = ");
|
||||
query.push_bind(output_format);
|
||||
has_updates = true;
|
||||
}
|
||||
|
||||
if !has_updates {
|
||||
// No updates requested, fetch and return existing action
|
||||
return Self::find_by_id(executor, id)
|
||||
@@ -276,7 +295,7 @@ impl Update for ActionRepository {
|
||||
|
||||
query.push(", updated = NOW() WHERE id = ");
|
||||
query.push_bind(id);
|
||||
query.push(" RETURNING id, ref, pack, pack_ref, label, description, entrypoint, runtime, runtime_version_constraint, param_schema, out_schema, workflow_def, is_adhoc, created, updated");
|
||||
query.push(&format!(" RETURNING {}", ACTION_COLUMNS));
|
||||
|
||||
let action = query
|
||||
.build_query_as::<Action>()
|
||||
@@ -317,10 +336,8 @@ impl ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + Copy + 'e,
|
||||
{
|
||||
let select_cols = "id, ref, pack, pack_ref, label, description, entrypoint, runtime, runtime_version_constraint, param_schema, out_schema, workflow_def, is_adhoc, created, updated";
|
||||
|
||||
let mut qb: QueryBuilder<'_, Postgres> =
|
||||
QueryBuilder::new(format!("SELECT {select_cols} FROM action"));
|
||||
QueryBuilder::new(format!("SELECT {} FROM action", ACTION_COLUMNS));
|
||||
let mut count_qb: QueryBuilder<'_, Postgres> =
|
||||
QueryBuilder::new("SELECT COUNT(*) FROM action");
|
||||
|
||||
@@ -398,16 +415,10 @@ impl ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let actions = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE pack = $1
|
||||
ORDER BY ref ASC
|
||||
"#,
|
||||
)
|
||||
let actions = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE pack = $1 ORDER BY ref ASC",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(pack_id)
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
@@ -420,16 +431,10 @@ impl ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let actions = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE runtime = $1
|
||||
ORDER BY ref ASC
|
||||
"#,
|
||||
)
|
||||
let actions = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE runtime = $1 ORDER BY ref ASC",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(runtime_id)
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
@@ -443,16 +448,10 @@ impl ActionRepository {
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let search_pattern = format!("%{}%", query.to_lowercase());
|
||||
let actions = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE LOWER(ref) LIKE $1 OR LOWER(label) LIKE $1 OR LOWER(description) LIKE $1
|
||||
ORDER BY ref ASC
|
||||
"#,
|
||||
)
|
||||
let actions = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE LOWER(ref) LIKE $1 OR LOWER(label) LIKE $1 OR LOWER(description) LIKE $1 ORDER BY ref ASC",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(&search_pattern)
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
@@ -465,16 +464,10 @@ impl ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let actions = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE workflow_def IS NOT NULL
|
||||
ORDER BY ref ASC
|
||||
"#,
|
||||
)
|
||||
let actions = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE workflow_def IS NOT NULL ORDER BY ref ASC",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
|
||||
@@ -489,15 +482,10 @@ impl ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let action = sqlx::query_as::<_, Action>(
|
||||
r#"
|
||||
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
FROM action
|
||||
WHERE workflow_def = $1
|
||||
"#,
|
||||
)
|
||||
let action = sqlx::query_as::<_, Action>(&format!(
|
||||
"SELECT {} FROM action WHERE workflow_def = $1",
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(workflow_def_id)
|
||||
.fetch_optional(executor)
|
||||
.await?;
|
||||
@@ -505,6 +493,36 @@ impl ActionRepository {
|
||||
Ok(action)
|
||||
}
|
||||
|
||||
/// Delete non-adhoc actions belonging to a pack whose refs are NOT in the given set.
|
||||
///
|
||||
/// Used during pack reinstallation to clean up actions that were removed
|
||||
/// from the pack's YAML files. Ad-hoc (user-created) actions are preserved.
|
||||
pub async fn delete_non_adhoc_by_pack_excluding<'e, E>(
|
||||
executor: E,
|
||||
pack_id: Id,
|
||||
keep_refs: &[String],
|
||||
) -> Result<u64>
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let result = if keep_refs.is_empty() {
|
||||
sqlx::query("DELETE FROM action WHERE pack = $1 AND is_adhoc = false")
|
||||
.bind(pack_id)
|
||||
.execute(executor)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query(
|
||||
"DELETE FROM action WHERE pack = $1 AND is_adhoc = false AND ref != ALL($2)",
|
||||
)
|
||||
.bind(pack_id)
|
||||
.bind(keep_refs)
|
||||
.execute(executor)
|
||||
.await?
|
||||
};
|
||||
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
|
||||
/// Link an action to a workflow definition
|
||||
pub async fn link_workflow_def<'e, E>(
|
||||
executor: E,
|
||||
@@ -514,16 +532,15 @@ impl ActionRepository {
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let action = sqlx::query_as::<_, Action>(
|
||||
let action = sqlx::query_as::<_, Action>(&format!(
|
||||
r#"
|
||||
UPDATE action
|
||||
SET workflow_def = $2, updated = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, ref, pack, pack_ref, label, description, entrypoint,
|
||||
runtime, runtime_version_constraint,
|
||||
param_schema, out_schema, workflow_def, is_adhoc, created, updated
|
||||
RETURNING {}
|
||||
"#,
|
||||
)
|
||||
ACTION_COLUMNS
|
||||
))
|
||||
.bind(action_id)
|
||||
.bind(workflow_def_id)
|
||||
.fetch_one(executor)
|
||||
|
||||
@@ -52,6 +52,7 @@ pub struct UpdateArtifactInput {
|
||||
pub description: Option<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub size_bytes: Option<i64>,
|
||||
pub execution: Option<Option<i64>>,
|
||||
pub data: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
@@ -189,6 +190,15 @@ impl Update for ArtifactRepository {
|
||||
push_field!(&input.description, "description");
|
||||
push_field!(&input.content_type, "content_type");
|
||||
push_field!(input.size_bytes, "size_bytes");
|
||||
// execution is Option<Option<i64>> — outer Option = "was field provided?",
|
||||
// inner Option = nullable column value
|
||||
if let Some(exec_val) = input.execution {
|
||||
if has_updates {
|
||||
query.push(", ");
|
||||
}
|
||||
query.push("execution = ").push_bind(exec_val);
|
||||
has_updates = true;
|
||||
}
|
||||
push_field!(&input.data, "data");
|
||||
|
||||
if !has_updates {
|
||||
|
||||
@@ -284,6 +284,34 @@ impl RuntimeRepository {
|
||||
Ok(runtime)
|
||||
}
|
||||
|
||||
/// Delete runtimes belonging to a pack whose refs are NOT in the given set.
|
||||
///
|
||||
/// Used during pack reinstallation to clean up runtimes that were removed
|
||||
/// from the pack's YAML files. Associated runtime_version rows are
|
||||
/// cascade-deleted by the FK constraint.
|
||||
pub async fn delete_by_pack_excluding<'e, E>(
|
||||
executor: E,
|
||||
pack_id: Id,
|
||||
keep_refs: &[String],
|
||||
) -> Result<u64>
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let result = if keep_refs.is_empty() {
|
||||
sqlx::query("DELETE FROM runtime WHERE pack = $1")
|
||||
.bind(pack_id)
|
||||
.execute(executor)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query("DELETE FROM runtime WHERE pack = $1 AND ref != ALL($2)")
|
||||
.bind(pack_id)
|
||||
.bind(keep_refs)
|
||||
.execute(executor)
|
||||
.await?
|
||||
};
|
||||
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -301,6 +301,36 @@ impl Delete for TriggerRepository {
|
||||
}
|
||||
|
||||
impl TriggerRepository {
|
||||
/// Delete non-adhoc triggers belonging to a pack whose refs are NOT in the given set.
|
||||
///
|
||||
/// Used during pack reinstallation to clean up triggers that were removed
|
||||
/// from the pack's YAML files. Ad-hoc (user-created) triggers are preserved.
|
||||
pub async fn delete_non_adhoc_by_pack_excluding<'e, E>(
|
||||
executor: E,
|
||||
pack_id: Id,
|
||||
keep_refs: &[String],
|
||||
) -> Result<u64>
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let result = if keep_refs.is_empty() {
|
||||
sqlx::query("DELETE FROM trigger WHERE pack = $1 AND is_adhoc = false")
|
||||
.bind(pack_id)
|
||||
.execute(executor)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query(
|
||||
"DELETE FROM trigger WHERE pack = $1 AND is_adhoc = false AND ref != ALL($2)",
|
||||
)
|
||||
.bind(pack_id)
|
||||
.bind(keep_refs)
|
||||
.execute(executor)
|
||||
.await?
|
||||
};
|
||||
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
|
||||
/// Search triggers with all filters pushed into SQL.
|
||||
///
|
||||
/// All filter fields are combinable (AND). Pagination is server-side.
|
||||
@@ -907,6 +937,34 @@ impl Delete for SensorRepository {
|
||||
}
|
||||
|
||||
impl SensorRepository {
|
||||
/// Delete non-adhoc sensors belonging to a pack whose refs are NOT in the given set.
|
||||
///
|
||||
/// Used during pack reinstallation to clean up sensors that were removed
|
||||
/// from the pack's YAML files.
|
||||
pub async fn delete_by_pack_excluding<'e, E>(
|
||||
executor: E,
|
||||
pack_id: Id,
|
||||
keep_refs: &[String],
|
||||
) -> Result<u64>
|
||||
where
|
||||
E: Executor<'e, Database = Postgres> + 'e,
|
||||
{
|
||||
let result = if keep_refs.is_empty() {
|
||||
sqlx::query("DELETE FROM sensor WHERE pack = $1")
|
||||
.bind(pack_id)
|
||||
.execute(executor)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query("DELETE FROM sensor WHERE pack = $1 AND ref != ALL($2)")
|
||||
.bind(pack_id)
|
||||
.bind(keep_refs)
|
||||
.execute(executor)
|
||||
.await?
|
||||
};
|
||||
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
|
||||
/// Search sensors with all filters pushed into SQL.
|
||||
///
|
||||
/// All filter fields are combinable (AND). Pagination is server-side.
|
||||
|
||||
Reference in New Issue
Block a user