use derive_builder::Builder;
use crate::api::common::NameOrId;
use crate::api::endpoint_prelude::*;
use crate::api::ParamValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum IssueLinkType {
RelatesTo,
Blocks,
IsBlockedBy,
}
impl IssueLinkType {
fn as_str(self) -> &'static str {
match self {
IssueLinkType::RelatesTo => "relates_to",
IssueLinkType::Blocks => "blocks",
IssueLinkType::IsBlockedBy => "is_blocked_by",
}
}
}
impl ParamValue<'static> for IssueLinkType {
fn as_value(&self) -> Cow<'static, str> {
self.as_str().into()
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option))]
pub struct CreateIssueLink<'a> {
#[builder(setter(into))]
project: NameOrId<'a>,
issue: u64,
#[builder(setter(into))]
target_project: NameOrId<'a>,
target_issue: u64,
#[builder(setter(into), default)]
link_type: Option<IssueLinkType>,
}
impl<'a> CreateIssueLink<'a> {
pub fn builder() -> CreateIssueLinkBuilder<'a> {
CreateIssueLinkBuilder::default()
}
}
impl Endpoint for CreateIssueLink<'_> {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
format!("projects/{}/issues/{}/links", self.project, self.issue).into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
let mut params = FormParams::default();
params
.push("target_project_id", &self.target_project)
.push("target_issue_iid", self.target_issue)
.push_opt("link_type", self.link_type);
params.into_body()
}
}
#[cfg(test)]
mod tests {
use http::Method;
use crate::api::projects::issues::links::{
CreateIssueLink, CreateIssueLinkBuilderError, IssueLinkType,
};
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};
#[test]
fn issue_link_type_as_str() {
let items = &[
(IssueLinkType::RelatesTo, "relates_to"),
(IssueLinkType::Blocks, "blocks"),
(IssueLinkType::IsBlockedBy, "is_blocked_by"),
];
for (i, s) in items {
assert_eq!(i.as_str(), *s);
}
}
#[test]
fn project_issue_and_targets_are_necessary() {
let err = CreateIssueLink::builder().build().unwrap_err();
crate::test::assert_missing_field!(err, CreateIssueLinkBuilderError, "project");
}
#[test]
fn project_is_necessary() {
let err = CreateIssueLink::builder()
.issue(1)
.target_project(2)
.target_issue(2)
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateIssueLinkBuilderError, "project");
}
#[test]
fn issue_is_necessary() {
let err = CreateIssueLink::builder()
.project(1)
.target_project(2)
.target_issue(2)
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateIssueLinkBuilderError, "issue");
}
#[test]
fn target_project_is_necessary() {
let err = CreateIssueLink::builder()
.project(1)
.issue(1)
.target_issue(2)
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateIssueLinkBuilderError, "target_project");
}
#[test]
fn target_issue_is_necessary() {
let err = CreateIssueLink::builder()
.project(1)
.issue(1)
.target_project(2)
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateIssueLinkBuilderError, "target_issue");
}
#[test]
fn project_issue_and_targets_are_sufficient() {
CreateIssueLink::builder()
.project(1)
.issue(1)
.target_project(2)
.target_issue(2)
.build()
.unwrap();
}
#[test]
fn endpoint() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/issues/1/links")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("target_project_id=4", "&target_issue_iid=10"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateIssueLink::builder()
.project("simple/project")
.issue(1)
.target_project(4)
.target_issue(10)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_link_type() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/issues/1/links")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"target_project_id=simple%2Fproject",
"&target_issue_iid=10",
"&link_type=blocks",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateIssueLink::builder()
.project("simple/project")
.issue(1)
.target_project("simple/project")
.target_issue(10)
.link_type(IssueLinkType::Blocks)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}