use derive_builder::Builder;
use crate::api::common::CommaSeparatedList;
use crate::api::endpoint_prelude::*;
use crate::api::runners::{RunnerAccessLevel, RunnerType};
#[derive(Debug, Clone, Copy)]
enum RunnerHome {
Instance,
Project(u64),
Group(u64),
}
impl RunnerHome {
fn runner_type(self) -> RunnerType {
match self {
Self::Instance => RunnerType::Instance,
Self::Project(_) => RunnerType::Project,
Self::Group(_) => RunnerType::Group,
}
}
fn add_query(self, params: &mut FormParams) {
params.push("runner_type", self.runner_type());
match self {
Self::Instance => (),
Self::Project(id) => {
params.push("project_id", id);
},
Self::Group(id) => {
params.push("group_id", id);
},
}
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
pub struct CreateRunner<'a> {
#[builder(setter(name = "_runner_home"), private)]
runner_home: RunnerHome,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[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 instance(&mut self) -> &mut Self {
self.runner_home = Some(RunnerHome::Instance);
self
}
pub fn project(&mut self, project_id: u64) -> &mut Self {
self.runner_home = Some(RunnerHome::Project(project_id));
self
}
pub fn group(&mut self, group_id: u64) -> &mut Self {
self.runner_home = Some(RunnerHome::Group(group_id));
self
}
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> {
use crate::api::runners::MAX_MAINTENANCE_NOTE_LENGTH;
if let Some(Some(maintenance_note)) = self.maintenance_note.as_ref() {
if maintenance_note.len() > MAX_MAINTENANCE_NOTE_LENGTH {
return Err(format!(
"`maintenance_note` may be at most {MAX_MAINTENANCE_NOTE_LENGTH} bytes",
)
.into());
}
}
Ok(())
}
}
impl Endpoint for CreateRunner<'_> {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
"user/runners".into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
let mut params = FormParams::default();
self.runner_home.add_query(&mut params);
params
.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());
params.into_body()
}
}
#[cfg(test)]
mod tests {
use http::Method;
use crate::api::runners::RunnerAccessLevel;
use crate::api::users::runners::{CreateRunner, CreateRunnerBuilderError};
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};
#[test]
fn runner_type_is_required() {
let err = CreateRunner::builder().build().unwrap_err();
crate::test::assert_missing_field!(err, CreateRunnerBuilderError, "runner_home");
}
#[test]
fn runner_type_is_sufficient() {
CreateRunner::builder().instance().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()
.instance()
.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("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str("runner_type=instance_type")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder().instance().build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_group_type() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("runner_type=group_type", "&group_id=123"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder().group(123).build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_project_type() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("runner_type=project_type", "&project_id=123"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder().project(123).build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_description() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("runner_type=instance_type", "&description=desc"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.description("desc")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_paused() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("runner_type=instance_type", "&paused=true"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.paused(true)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_locked() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("runner_type=instance_type", "&locked=false"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.locked(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_run_untagged() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("runner_type=instance_type", "&run_untagged=false"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.run_untagged(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_tag_list() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"runner_type=instance_type",
"&tag_list=tag2%2Ctag1%2Ctag3",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.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("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"runner_type=instance_type",
"&access_level=ref_protected",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.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("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"runner_type=instance_type",
"&maximum_timeout=3600"
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.maximum_timeout(3600)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_maintenance_note() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("user/runners")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"runner_type=instance_type",
"&maintenance_note=note"
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateRunner::builder()
.instance()
.maintenance_note("note")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}