use derive_builder::Builder;
use crate::api::common::CommaSeparatedList;
use crate::api::endpoint_prelude::*;
use crate::api::runners::RunnerAccessLevel;
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option))]
pub struct RunnerMetadata<'a> {
#[builder(setter(into), default)]
name: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
version: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
platform: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
architecture: Option<Cow<'a, str>>,
}
impl<'a> RunnerMetadata<'a> {
pub fn builder() -> RunnerMetadataBuilder<'a> {
RunnerMetadataBuilder::default()
}
fn add_params<'b>(&'b self, params: &mut FormParams<'b>) {
params
.push_opt("info[name]", self.name.as_ref())
.push_opt("info[version]", self.version.as_ref())
.push_opt("info[platform]", self.platform.as_ref())
.push_opt("info[architecture]", self.architecture.as_ref());
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
pub struct CreateRunner<'a> {
#[builder(setter(into))]
token: Cow<'a, str>,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[builder(default)]
info: Option<RunnerMetadata<'a>>,
#[builder(default)]
paused: Option<bool>,
#[builder(default)]
locked: Option<bool>,
#[builder(default)]
run_untagged: Option<bool>,
#[builder(setter(name = "_tag_list"), default, private)]
tag_list: Option<CommaSeparatedList<Cow<'a, str>>>,
#[builder(default)]
access_level: Option<RunnerAccessLevel>,
#[builder(default)]
maximum_timeout: Option<u64>,
#[builder(setter(into), default)]
maintenance_note: Option<Cow<'a, str>>,
}
impl<'a> CreateRunner<'a> {
pub fn builder() -> CreateRunnerBuilder<'a> {
CreateRunnerBuilder::default()
}
}
impl<'a> CreateRunnerBuilder<'a> {
pub fn tag<T>(&mut self, tag: T) -> &mut Self
where
T: Into<Cow<'a, str>>,
{
self.tag_list
.get_or_insert(None)
.get_or_insert_with(CommaSeparatedList::new)
.push(tag.into());
self
}
pub fn tags<I, T>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = T>,
T: Into<Cow<'a, str>>,
{
self.tag_list
.get_or_insert(None)
.get_or_insert_with(CommaSeparatedList::new)
.extend(iter.map(|t| t.into()));
self
}
fn validate(&self) -> Result<(), CreateRunnerBuilderError> {
if let Some(Some(maintenance_note)) = self.maintenance_note.as_ref() {
if maintenance_note.len() > super::MAX_MAINTENANCE_NOTE_LENGTH {
return Err(format!(
"`maintenance_note` may be at most {} bytes",
super::MAX_MAINTENANCE_NOTE_LENGTH,
)
.into());
}
}
Ok(())
}
}
impl Endpoint for CreateRunner<'_> {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
"runners".into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
let mut params = FormParams::default();
params
.push("token", &self.token)
.push_opt("description", self.description.as_ref())
.push_opt("paused", self.paused)
.push_opt("locked", self.locked)
.push_opt("run_untagged", self.run_untagged)
.push_opt("tag_list", self.tag_list.as_ref())
.push_opt("access_level", self.access_level)
.push_opt("maximum_timeout", self.maximum_timeout)
.push_opt("maintenance_note", self.maintenance_note.as_ref());
if let Some(info) = self.info.as_ref() {
info.add_params(&mut params);
}
params.into_body()
}
}
#[cfg(test)]
mod tests {
use http::Method;
use crate::api::runners::{
CreateRunner, CreateRunnerBuilderError, RunnerAccessLevel, RunnerMetadata,
};
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};
#[test]
fn runner_metadata_default_is_sufficient() {
RunnerMetadata::builder().build().unwrap();
}
#[test]
fn token_is_required() {
let err = CreateRunner::builder().build().unwrap_err();
crate::test::assert_missing_field!(err, CreateRunnerBuilderError, "token");
}
#[test]
fn token_is_sufficient() {
CreateRunner::builder().token("tok").build().unwrap();
}
#[test]
fn maintenance_note_length() {
use crate::api::runners::MAX_MAINTENANCE_NOTE_LENGTH;
let too_long = format!("{:width$}", "note", width = MAX_MAINTENANCE_NOTE_LENGTH + 1);
let err = CreateRunner::builder()
.token("tok")
.maintenance_note(too_long)
.build()
.unwrap_err();
if let CreateRunnerBuilderError::ValidationError(message) = err {
assert_eq!(
message,
format!("`maintenance_note` may be at most {MAX_MAINTENANCE_NOTE_LENGTH} bytes"),
);
} else {
panic!("unexpected error: {:?}", err);
}
}
#[test]
fn endpoint() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str("token=tok")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder().token("tok").build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_description() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&description=desc"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.description("desc")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_info() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"token=tok",
"&info%5Bname%5D=name",
"&info%5Bversion%5D=1.0.0",
"&info%5Bplatform%5D=linux",
"&info%5Barchitecture%5D=x86_64",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.info(
RunnerMetadata::builder()
.name("name")
.version("1.0.0")
.platform("linux")
.architecture("x86_64")
.build()
.unwrap(),
)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_paused() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&paused=true"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.paused(true)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_locked() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&locked=false"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.locked(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_run_untagged() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&run_untagged=false"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.run_untagged(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_tag_list() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&tag_list=tag2%2Ctag1%2Ctag3"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.tag("tag2")
.tags(["tag1", "tag3"].iter().cloned())
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_access_level() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&access_level=ref_protected"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.access_level(RunnerAccessLevel::RefProtected)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_maximum_timeout() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&maximum_timeout=3600"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.maximum_timeout(3600)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_maintenance_note() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("token=tok", "&maintenance_note=note"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.token("tok")
.maintenance_note("note")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}