Skip to content
41 changes: 11 additions & 30 deletions common/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ impl Client {
url
}

fn get(&self, path: Cow<'static, str>) -> crate::http::RequestBuilder {
let mut req = self.client.get(self.url_join(&path));
fn add_authentication(&self, mut req: RequestBuilder) -> RequestBuilder {
if let Some(auth_cookie) = &self.auth_cookie {
req = req.header(AUTH_COOKIE_HEADER, auth_cookie);
}
Expand All @@ -121,38 +120,20 @@ impl Client {
req
}

fn post(&self, path: Cow<'static, str>) -> crate::http::RequestBuilder {
let mut req = self.client.post(self.url_join(&path));
if let Some(auth_cookie) = &self.auth_cookie {
req = req.header(AUTH_COOKIE_HEADER, auth_cookie);
}

if let Some(worker_key) = &self.worker_key {
req = req.header(WORKER_KEY_HEADER, worker_key);
}

if let Some(signup_secret) = &self.signup_secret {
req = req.header(SIGNUP_SECRET_HEADER, signup_secret);
}

req
fn get(&self, path: Cow<'static, str>) -> RequestBuilder {
self.add_authentication(self.client.get(self.url_join(&path)))
}

fn delete(&self, path: Cow<'static, str>) -> crate::http::RequestBuilder {
let mut req = self.client.delete(self.url_join(&path));
if let Some(auth_cookie) = &self.auth_cookie {
req = req.header(AUTH_COOKIE_HEADER, auth_cookie);
}

if let Some(worker_key) = &self.worker_key {
req = req.header(WORKER_KEY_HEADER, worker_key);
}
fn post(&self, path: Cow<'static, str>) -> RequestBuilder {
self.add_authentication(self.client.post(self.url_join(&path)))
}

if let Some(signup_secret) = &self.signup_secret {
req = req.header(SIGNUP_SECRET_HEADER, signup_secret);
}
fn put(&self, path: Cow<'static, str>) -> RequestBuilder {
self.add_authentication(self.client.put(self.url_join(&path)))
}

req
fn delete(&self, path: Cow<'static, str>) -> RequestBuilder {
self.add_authentication(self.client.delete(self.url_join(&path)))
}
}

Expand Down
125 changes: 125 additions & 0 deletions common/src/api/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,20 @@ pub trait WorkerRestApi {
async fn register_worker(&self, request: RegisterWorkerRequest) -> Result<()>;
async fn get_worker(&self, id: i32) -> Result<Worker>;
async fn unregister_worker(&self, id: i32) -> Result<()>;
async fn get_worker_tags(&self, id: i32) -> Result<Vec<String>>;
async fn set_worker_tags(&self, id: i32, tags: Vec<String>) -> Result<()>;
async fn create_worker_tag(&self, id: i32, tag: String) -> Result<()>;
async fn delete_worker_tag(&self, id: i32, tag: String) -> Result<()>;
}

#[async_trait]
pub trait TagRestApi {
async fn get_tags(&self) -> Result<Vec<String>>;
async fn create_tag(&self, request: CreateTagRequest) -> Result<String>;
async fn delete_tag(&self, tag: String) -> Result<()>;
async fn get_tag_rules(&self, tag: String) -> Result<Vec<TagRule>>;
async fn create_tag_rule(&self, tag: String, request: CreateTagRuleRequest) -> Result<TagRule>;
async fn delete_tag_rule(&self, tag: String, tag_rule_id: i32) -> Result<()>;
}

#[async_trait]
Expand Down Expand Up @@ -664,4 +678,115 @@ impl WorkerRestApi for Client {

Ok(())
}

async fn get_worker_tags(&self, id: i32) -> Result<Vec<String>> {
let tags = self
.get(Cow::Owned(format!("api/v1/workers/{id}/tags")))
.send()
.await?
.error_for_status()?
.json()
.await?;

Ok(tags)
}

async fn set_worker_tags(&self, id: i32, tags: Vec<String>) -> Result<()> {
self.put(Cow::Owned(format!("api/v1/workers/{id}/tags")))
.json(&tags)
.send_encoded()
.await?
.error_for_status()?;

Ok(())
}

async fn create_worker_tag(&self, id: i32, tag: String) -> Result<()> {
self.put(Cow::Owned(format!("api/v1/workers/{id}/tags/{tag}")))
.send()
.await?
.error_for_status()?;

Ok(())
}

async fn delete_worker_tag(&self, id: i32, tag: String) -> Result<()> {
self.delete(Cow::Owned(format!("api/v1/workers/{id}/tags/{tag}")))
.send()
.await?
.error_for_status()?;

Ok(())
}
}

#[async_trait]
impl TagRestApi for Client {
async fn get_tags(&self) -> Result<Vec<String>> {
let records = self
.get(Cow::Borrowed("api/v1/tags"))
.send()
.await?
.error_for_status()?
.json()
.await?;

Ok(records)
}

async fn create_tag(&self, request: CreateTagRequest) -> Result<String> {
let record = self
.post(Cow::Borrowed("api/v1/tags"))
.json(&request)
.send_encoded()
.await?
.error_for_status()?
.json()
.await?;

Ok(record)
}

async fn delete_tag(&self, tag: String) -> Result<()> {
self.delete(Cow::Owned(format!("api/v1/tags/{tag}")))
.send()
.await?
.error_for_status()?;

Ok(())
}

async fn get_tag_rules(&self, tag: String) -> Result<Vec<TagRule>> {
let records = self
.get(Cow::Owned(format!("api/v1/tags/{tag}")))
.send()
.await?
.error_for_status()?
.json()
.await?;

Ok(records)
}

async fn create_tag_rule(&self, tag: String, request: CreateTagRuleRequest) -> Result<TagRule> {
let record = self
.post(Cow::Owned(format!("api/v1/tags/{tag}")))
.json(&request)
.send_encoded()
.await?
.error_for_status()?
.json()
.await?;

Ok(record)
}

async fn delete_tag_rule(&self, tag: String, tag_rule_id: i32) -> Result<()> {
self.delete(Cow::Owned(format!("api/v1/tags/{tag}/{tag_rule_id}")))
.send()
.await?
.error_for_status()?;

Ok(())
}
}
2 changes: 2 additions & 0 deletions common/src/api/v1/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod dashboard;
mod meta;
mod package;
mod queue;
mod tag;
mod worker;

pub use build::*;
Expand All @@ -11,6 +12,7 @@ pub use meta::*;
pub use package::*;
pub use queue::*;
use serde::{Deserialize, Serialize};
pub use tag::*;
pub use worker::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
23 changes: 23 additions & 0 deletions common/src/api/v1/models/tag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[cfg(feature = "diesel")]
use diesel::Queryable;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateTagRequest {
pub tag: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateTagRuleRequest {
pub name_pattern: String,
pub version_pattern: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "diesel", derive(Queryable))]
#[cfg_attr(feature = "diesel", diesel(check_for_backend(diesel::sqlite::Sqlite)))]
pub struct TagRule {
pub id: i32,
pub name_pattern: String,
pub version_pattern: Option<String>,
}
3 changes: 3 additions & 0 deletions daemon/migrations/2025-11-01-202819_worker-tags/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DROP TABLE tag_rules;
DROP TABLE worker_tags;
DROP TABLE tags;
32 changes: 32 additions & 0 deletions daemon/migrations/2025-11-01-202819_worker-tags/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
CREATE TABLE tags
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
tag TEXT NOT NULL
);

CREATE UNIQUE INDEX tags_unique_idx ON tags (tag);

CREATE TABLE worker_tags
(
worker_id INTEGER NOT NULL REFERENCES workers ON DELETE CASCADE,
tag_id INTEGER NOT NULL REFERENCES tags ON DELETE CASCADE,

PRIMARY KEY (worker_id, tag_id)
);

CREATE INDEX worker_tags_worker_id_idx ON worker_tags (worker_id);
CREATE INDEX worker_tags_tag_id_idx ON worker_tags (tag_id);

CREATE TABLE tag_rules
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
tag_id INTEGER NOT NULL REFERENCES tags ON DELETE CASCADE,
name_pattern TEXT NOT NULL,
version_pattern TEXT
);

CREATE UNIQUE INDEX tag_rules_unique_idx ON tag_rules (tag_id,
name_pattern,
COALESCE(version_pattern, 'PLACEHOLDER'));

CREATE INDEX tag_rules_tag_id_idx ON tag_rules (tag_id);
2 changes: 2 additions & 0 deletions daemon/src/api/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod dashboard;
mod meta;
mod package;
mod queue;
mod tag;
mod util;
mod worker;

Expand All @@ -11,4 +12,5 @@ pub use dashboard::*;
pub use meta::*;
pub use package::*;
pub use queue::*;
pub use tag::*;
pub use worker::*;
Loading
Loading