Skip to content

Commit

Permalink
feat: msft teams support for critical alerts (#5113)
Browse files Browse the repository at this point in the history
* feat: msft teams support for critical alerts

* ee changes

* ee

* sqlx prep

* multiple teams channels

* commit file, not symlink

* improve reactivity

* docs link

* Update ee-repo-ref.txt
  • Loading branch information
alpetric authored Jan 23, 2025
1 parent e530855 commit a88fbb2
Show file tree
Hide file tree
Showing 29 changed files with 387 additions and 42 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/ee-repo-ref.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ddcfdfc18a9833a5fc4e62ad62a265ef1e06a0aa
0c89b8974ff6e1c9eda2134f09d4a03f18b57c15
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELETE FROM global_settings WHERE name = 'teams';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO global_settings (name, value) VALUES ('teams', '{}');
5 changes: 4 additions & 1 deletion backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use windmill_common::{
NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING,
REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING,
RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING,
TIMEOUT_WAIT_RESULT_SETTING,
TIMEOUT_WAIT_RESULT_SETTING, TEAMS_SETTING
},
scripts::ScriptLang,
stats_ee::schedule_stats,
Expand Down Expand Up @@ -734,6 +734,9 @@ Windmill Community Edition {GIT_VERSION}
SMTP_SETTING => {
reload_smtp_config(&db).await;
},
TEAMS_SETTING => {
tracing::info!("Teams setting changed.");
},
INDEXER_SETTING => {
reload_indexer_config(&db).await;
},
Expand Down
2 changes: 1 addition & 1 deletion backend/src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1479,7 +1479,7 @@ pub async fn reload_base_url_setting(db: &DB) -> error::Result<()> {
#[cfg(feature = "oauth2")]
{
let mut l = windmill_api::OAUTH_CLIENTS.write().await;
*l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths)
*l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths, db).await
.map_err(|e| tracing::error!("Error building oauth clients (is the oauth.json mounted and in correct format? Use '{}' as minimal oauth.json): {}", "{}", e))
.unwrap();
}
Expand Down
62 changes: 62 additions & 0 deletions backend/windmill-api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3234,6 +3234,22 @@ paths:
type: array
items:
type: string

/teams/sync:
post:
operationId: syncTeams
summary: synchronize Microsoft Teams information (teams/channels)
tags:
- teams
responses:
'200':
description: Teams information successfully synchronized
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TeamInfo'

/w/{workspace}/resources/create:
post:
Expand Down Expand Up @@ -14012,3 +14028,49 @@ components:
format: date-time
required:
- trigger_kind

TeamInfo:
type: object
required:
- team_id
- team_name
- channels
properties:
team_id:
type: string
description: The unique identifier of the Microsoft Teams team
example: "19:[email protected]"
team_name:
type: string
description: The display name of the Microsoft Teams team
example: "Engineering Team"
channels:
type: array
description: List of channels within the team
items:
$ref: '#/components/schemas/ChannelInfo'

ChannelInfo:
type: object
required:
- channel_id
- channel_name
- tenant_id
- service_url
properties:
channel_id:
type: string
description: The unique identifier of the channel
example: "19:[email protected]"
channel_name:
type: string
description: The display name of the channel
example: "General"
tenant_id:
type: string
description: The Microsoft Teams tenant identifier
example: "12345678-1234-1234-1234-123456789012"
service_url:
type: string
description: The service URL for the channel
example: "https://smba.trafficmanager.net/amer/12345678-1234-1234-1234-123456789012/"
6 changes: 2 additions & 4 deletions backend/windmill-api/src/ai.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::{
db::{ApiAuthed, DB},
variables::decrypt,
};
use crate::db::{ApiAuthed, DB};
use windmill_common::variables::decrypt;
use anthropic::AnthropicCache;
use axum::{
body::Bytes,
Expand Down
2 changes: 1 addition & 1 deletion backend/windmill-api/src/apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ use crate::{
resources::get_resource_value_interpolated_internal,
users::{require_owner_of_path, OptAuthed},
utils::WithStarredInfoQuery,
variables::encrypt,
webhook_util::{WebhookMessage, WebhookShared},
HTTP_CLIENT,
};
use windmill_common::variables::encrypt;
#[cfg(feature = "parquet")]
use crate::{
job_helpers_ee::{
Expand Down
13 changes: 13 additions & 0 deletions backend/windmill-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ mod scripts;
mod service_logs;
mod settings;
mod slack_approvals;
#[cfg(feature = "enterprise")]
mod teams_ee;
#[cfg(feature = "smtp")]
mod smtp_server_ee;
mod static_assets;
Expand Down Expand Up @@ -435,6 +437,17 @@ pub async fn run_server(
jobs::workspace_unauthed_service().layer(cors.clone()),
)
.route("/slack", post(slack_approvals::slack_app_callback_handler))
.nest("/teams", {
#[cfg(feature = "enterprise")]
{
teams_ee::teams_service()
}

#[cfg(not(feature = "enterprise"))]
{
Router::new()
}
})
.route(
"/w/:workspace_id/jobs/slack_approval/:job_id",
get(slack_approvals::request_slack_approval),
Expand Down
3 changes: 2 additions & 1 deletion backend/windmill-api/src/oauth2_ee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ pub struct AllClients {
}

#[cfg(feature = "oauth2")]
pub fn build_oauth_clients(
pub async fn build_oauth_clients(
_base_url: &str,
_oauths_from_config: Option<HashMap<String, OAuthClient>>,
_db: &DB,
) -> anyhow::Result<AllClients> {
// Implementation is not open source
return Ok(AllClients {
Expand Down
5 changes: 5 additions & 0 deletions backend/windmill-api/src/teams_ee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use axum::Router;

pub fn teams_service() -> Router {
Router::new()
}
16 changes: 1 addition & 15 deletions backend/windmill-api/src/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use windmill_common::{
};

use lazy_static::lazy_static;
use magic_crypt::{MagicCrypt256, MagicCryptError, MagicCryptTrait};
use windmill_common::variables::{decrypt, encrypt};
use serde::Deserialize;
use sqlx::{Postgres, Transaction};
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
Expand Down Expand Up @@ -676,17 +676,3 @@ pub async fn get_value_internal<'c>(

Ok(r)
}

pub fn encrypt(mc: &MagicCrypt256, value: &str) -> String {
mc.encrypt_str_to_base64(value)
}

pub fn decrypt(mc: &MagicCrypt256, value: String) -> Result<String> {
mc.decrypt_base64_to_string(value).map_err(|e| match e {
MagicCryptError::DecryptError(_) => Error::InternalErr(
"Could not decrypt value. The value may have been encrypted with a different key."
.to_string(),
),
_ => Error::InternalErr(e.to_string()),
})
}
2 changes: 1 addition & 1 deletion backend/windmill-api/src/workspaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use windmill_git_sync::handle_deployment_metadata;
#[cfg(feature = "enterprise")]
use windmill_common::utils::require_admin_or_devops;

use crate::variables::{decrypt, encrypt};
use windmill_common::variables::{decrypt, encrypt};
use hyper::StatusCode;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Postgres, Transaction};
Expand Down
2 changes: 1 addition & 1 deletion backend/windmill-api/src/workspaces_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use windmill_common::{
variables::ExportableListableVariable,
};

use crate::variables::decrypt;
use windmill_common::variables::decrypt;
use hyper::header;
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand Down
19 changes: 19 additions & 0 deletions backend/windmill-common/src/global_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub const PIP_INDEX_URL_SETTING: &str = "pip_index_url";
pub const SCIM_TOKEN_SETTING: &str = "scim_token";
pub const SAML_METADATA_SETTING: &str = "saml_metadata";
pub const SMTP_SETTING: &str = "smtp_settings";
pub const TEAMS_SETTING: &str = "teams";
pub const INDEXER_SETTING: &str = "indexer_settings";
pub const TIMEOUT_WAIT_RESULT_SETTING: &str = "timeout_wait_result";

Expand Down Expand Up @@ -94,3 +95,21 @@ pub const ENV_SETTINGS: [&str; 54] = [
"OTEL_TRACING",
"OTEL_LOGS",
];

use crate::error;
use sqlx::Pool;
use sqlx::postgres::Postgres;

pub async fn load_value_from_global_settings(
db: &Pool<Postgres>,
setting_name: &str,
) -> error::Result<Option<serde_json::Value>> {
let r = sqlx::query!(
"SELECT value FROM global_settings WHERE name = $1",
setting_name
)
.fetch_optional(db)
.await?
.map(|x| x.value);
Ok(r)
}
1 change: 1 addition & 0 deletions backend/windmill-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod job_s3_helpers_ee;
pub mod jobs;
pub mod more_serde;
pub mod oauth2;
pub mod teams_ee;
pub mod otel_ee;
pub mod queue;
pub mod s3_helpers;
Expand Down
3 changes: 3 additions & 0 deletions backend/windmill-common/src/oauth2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub const WORKSPACE_SLACK_BOT_TOKEN_PATH: &str = "f/slack_bot/bot_token";

pub const GLOBAL_SLACK_BOT_TOKEN_PATH: &str = "f/slack_bot/global_bot_token";

pub const GLOBAL_TEAMS_BOT_TOKEN_PATH: &str = "f/teams_bot/global_bot_token";

pub const GLOBAL_TEAMS_API_TOKEN_PATH: &str = "f/teams_bot/global_api_token";
lazy_static::lazy_static! {

pub static ref REQUIRE_PREEXISTING_USER_FOR_OAUTH: AtomicBool = AtomicBool::new(std::env::var("REQUIRE_PREEXISTING_USER_FOR_OAUTH")
Expand Down
Empty file.
15 changes: 15 additions & 0 deletions backend/windmill-common/src/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use chrono::{SecondsFormat, Utc};
use magic_crypt::{MagicCrypt256, MagicCryptError, MagicCryptTrait};
use serde::{Deserialize, Serialize};
use crate::error;

use crate::{worker::WORKER_GROUP, BASE_URL, DB};

Expand Down Expand Up @@ -157,6 +158,20 @@ pub async fn decrypt_value_with_mc(
})?)
}

pub fn encrypt(mc: &MagicCrypt256, value: &str) -> String {
mc.encrypt_str_to_base64(value)
}

pub fn decrypt(mc: &MagicCrypt256, value: String) -> error::Result<String> {
mc.decrypt_base64_to_string(value).map_err(|e| match e {
MagicCryptError::DecryptError(_) => error::Error::InternalErr(
"Could not decrypt value. The value may have been encrypted with a different key."
.to_string(),
),
_ => error::Error::InternalErr(e.to_string()),
})
}

pub const WM_SCHEDULED_FOR: &str = "WM_SCHEDULED_FOR";

pub async fn get_reserved_variables(
Expand Down
Loading

0 comments on commit a88fbb2

Please sign in to comment.