mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-10 16:05:58 +00:00
* chore: upgrade axum 0.7 to 0.8 and related dependencies Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add route reachability tests for ~80 previously untested endpoints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: switch feature-gated trigger handlers from axum::async_trait to async_trait crate Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update new trash routes to axum 0.8 path syntax Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to latest EE commit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: upgrade route tests to assert 2xx responses with proper data setup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: restore npm_proxy and ai_routes tests using local echo servers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: gate workspace fork test behind enterprise feature flag Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add ~40 more endpoint tests (jobs authed, health, favorites, ACLs, reachability) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review findings from axum 0.8 upgrade - Use cookie value_trimmed() instead of value() for cookie 0.18 compat - Update comments still referencing old :workspace_id syntax Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 61ae055ea31481f1899953e9d5f65566b8c707b1 This commit updates the EE repository reference after PR #486 was merged in windmill-ee-private. Previous ee-repo-ref: 0059d175a6fdddf52998b183bf91059b224704ac New ee-repo-ref: 61ae055ea31481f1899953e9d5f65566b8c707b1 Automated by sync-ee-ref workflow. * test: add test for new get_imports endpoint Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove unused import in raw_apps test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
241 lines
7.5 KiB
Rust
241 lines
7.5 KiB
Rust
use async_trait::async_trait;
|
|
use itertools::Itertools;
|
|
use sqlx::{types::Json as SqlxJson, PgConnection};
|
|
use windmill_api_auth::ApiAuthed;
|
|
use windmill_common::DB;
|
|
use windmill_common::{
|
|
db::UserDB,
|
|
error::{Error, Result},
|
|
};
|
|
use windmill_git_sync::DeployedObject;
|
|
use windmill_store::resources::try_get_resource_from_db_as;
|
|
use windmill_trigger::{Trigger, TriggerCrud, TriggerData};
|
|
|
|
use super::{
|
|
MqttClientBuilder, MqttClientVersion, MqttConfig, MqttConfigRequest, MqttResource, MqttTrigger,
|
|
MqttV3Config, MqttV5Config, SubscribeTopic, TestMqttConfig,
|
|
};
|
|
|
|
#[async_trait]
|
|
impl TriggerCrud for MqttTrigger {
|
|
type TriggerConfig = MqttConfig;
|
|
type Trigger = Trigger<Self::TriggerConfig>;
|
|
type TriggerConfigRequest = MqttConfigRequest;
|
|
type TestConnectionConfig = TestMqttConfig;
|
|
|
|
const TABLE_NAME: &'static str = "mqtt_trigger";
|
|
const TRIGGER_TYPE: &'static str = "mqtt";
|
|
const SUPPORTS_SERVER_STATE: bool = true;
|
|
const SUPPORTS_TEST_CONNECTION: bool = true;
|
|
const ROUTE_PREFIX: &'static str = "/mqtt_triggers";
|
|
const DEPLOYMENT_NAME: &'static str = "MQTT trigger";
|
|
const ADDITIONAL_SELECT_FIELDS: &[&'static str] = &[
|
|
"mqtt_resource_path",
|
|
"subscribe_topics",
|
|
"v3_config",
|
|
"v5_config",
|
|
"client_id",
|
|
"client_version",
|
|
];
|
|
const IS_ALLOWED_ON_CLOUD: bool = false;
|
|
|
|
fn get_deployed_object(path: String, parent_path: Option<String>) -> DeployedObject {
|
|
DeployedObject::MqttTrigger { path, parent_path }
|
|
}
|
|
|
|
async fn validate_config(
|
|
&self,
|
|
_db: &DB,
|
|
config: &Self::TriggerConfigRequest,
|
|
_workspace_id: &str,
|
|
) -> Result<()> {
|
|
if config.mqtt_resource_path.trim().is_empty() {
|
|
return Err(Error::BadRequest(
|
|
"MQTT resource path cannot be empty".to_string(),
|
|
));
|
|
}
|
|
|
|
if config.subscribe_topics.is_empty() {
|
|
return Err(Error::BadRequest(
|
|
"At least one subscribe topic must be specified".to_string(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn create_trigger(
|
|
&self,
|
|
_db: &DB,
|
|
tx: &mut PgConnection,
|
|
authed: &ApiAuthed,
|
|
w_id: &str,
|
|
trigger: TriggerData<Self::TriggerConfigRequest>,
|
|
) -> Result<()> {
|
|
let resolved_edited_by = trigger.base.resolve_edited_by(authed);
|
|
let resolved_permissioned_as = trigger.base.resolve_permissioned_as(authed);
|
|
let subscribe_topics = trigger
|
|
.config
|
|
.subscribe_topics
|
|
.into_iter()
|
|
.map(SqlxJson)
|
|
.collect_vec();
|
|
let v3_config = trigger.config.v3_config.map(SqlxJson);
|
|
let v5_config = trigger.config.v5_config.map(SqlxJson);
|
|
|
|
sqlx::query!(
|
|
r#"
|
|
INSERT INTO mqtt_trigger (
|
|
mqtt_resource_path,
|
|
subscribe_topics,
|
|
client_version,
|
|
client_id,
|
|
v3_config,
|
|
v5_config,
|
|
workspace_id,
|
|
path,
|
|
script_path,
|
|
is_flow,
|
|
permissioned_as,
|
|
mode,
|
|
edited_by,
|
|
error_handler_path,
|
|
error_handler_args,
|
|
retry
|
|
)
|
|
VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16
|
|
)"#,
|
|
trigger.config.mqtt_resource_path,
|
|
subscribe_topics.as_slice() as &[SqlxJson<SubscribeTopic>],
|
|
trigger.config.client_version as Option<MqttClientVersion>,
|
|
trigger.config.client_id,
|
|
v3_config as Option<SqlxJson<MqttV3Config>>,
|
|
v5_config as Option<SqlxJson<MqttV5Config>>,
|
|
w_id,
|
|
trigger.base.path,
|
|
trigger.base.script_path,
|
|
trigger.base.is_flow,
|
|
resolved_permissioned_as,
|
|
trigger.base.mode() as _,
|
|
&resolved_edited_by,
|
|
trigger.error_handling.error_handler_path,
|
|
trigger.error_handling.error_handler_args as _,
|
|
trigger.error_handling.retry as _
|
|
)
|
|
.execute(tx)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn update_trigger(
|
|
&self,
|
|
_db: &DB,
|
|
tx: &mut PgConnection,
|
|
authed: &ApiAuthed,
|
|
workspace_id: &str,
|
|
path: &str,
|
|
trigger: TriggerData<Self::TriggerConfigRequest>,
|
|
) -> Result<()> {
|
|
let resolved_edited_by = trigger.base.resolve_edited_by(authed);
|
|
let resolved_permissioned_as = trigger.base.resolve_permissioned_as(authed);
|
|
let subscribe_topics = trigger
|
|
.config
|
|
.subscribe_topics
|
|
.into_iter()
|
|
.map(SqlxJson)
|
|
.collect_vec();
|
|
let v3_config = trigger.config.v3_config.map(SqlxJson);
|
|
let v5_config = trigger.config.v5_config.map(SqlxJson);
|
|
|
|
// Important to set server_id to NULL to stop current mqtt listener
|
|
sqlx::query!(
|
|
r#"
|
|
UPDATE
|
|
mqtt_trigger
|
|
SET
|
|
mqtt_resource_path = $1,
|
|
subscribe_topics = $2,
|
|
client_version = $3,
|
|
client_id = $4,
|
|
v3_config = $5,
|
|
v5_config = $6,
|
|
is_flow = $7,
|
|
edited_by = $8,
|
|
permissioned_as = $9,
|
|
script_path = $10,
|
|
path = $11,
|
|
edited_at = now(),
|
|
error = NULL,
|
|
server_id = NULL,
|
|
error_handler_path = $14,
|
|
error_handler_args = $15,
|
|
retry = $16
|
|
WHERE
|
|
workspace_id = $12 AND
|
|
path = $13
|
|
"#,
|
|
trigger.config.mqtt_resource_path,
|
|
subscribe_topics.as_slice() as &[SqlxJson<SubscribeTopic>],
|
|
trigger.config.client_version as Option<MqttClientVersion>,
|
|
trigger.config.client_id,
|
|
v3_config as Option<SqlxJson<MqttV3Config>>,
|
|
v5_config as Option<SqlxJson<MqttV5Config>>,
|
|
trigger.base.is_flow,
|
|
&resolved_edited_by,
|
|
resolved_permissioned_as,
|
|
trigger.base.script_path,
|
|
trigger.base.path,
|
|
workspace_id,
|
|
path,
|
|
trigger.error_handling.error_handler_path,
|
|
trigger.error_handling.error_handler_args as _,
|
|
trigger.error_handling.retry as _
|
|
)
|
|
.execute(tx)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn test_connection(
|
|
&self,
|
|
db: &DB,
|
|
authed: &ApiAuthed,
|
|
user_db: &UserDB,
|
|
workspace_id: &str,
|
|
config: Self::TestConnectionConfig,
|
|
) -> Result<()> {
|
|
let mqtt_resource = try_get_resource_from_db_as::<MqttResource>(
|
|
authed,
|
|
Some(user_db.clone()),
|
|
db,
|
|
&config.mqtt_resource_path,
|
|
workspace_id,
|
|
)
|
|
.await?;
|
|
|
|
let connect_f = async {
|
|
let client_builder = MqttClientBuilder::new(
|
|
mqtt_resource,
|
|
Some(""),
|
|
vec![],
|
|
config.v3_config.as_ref(),
|
|
config.v5_config.as_ref(),
|
|
config.client_version.as_ref(),
|
|
);
|
|
|
|
client_builder.build_client().await.map_err(|err| {
|
|
Error::BadConfig(format!(
|
|
"Error connecting to mqtt broker: {}",
|
|
err.to_string()
|
|
))
|
|
})
|
|
};
|
|
|
|
connect_f.await?;
|
|
Ok(())
|
|
}
|
|
}
|