Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use anyhow::Result;
use std::rc::Rc;

use crate::domain::{issue::IssueId, repo::RepoId, repository::IssueRepository};
use crate::domain::{issue::OpenIssue, repo::RepoId, repository::IssueRepository};

#[derive(Clone)]
pub struct GetIssuesQuery<R: IssueRepository> {
pub repo: Rc<R>,
}

impl<R: IssueRepository> GetIssuesQuery<R> {
pub async fn execute(&self, repo_id: &RepoId) -> Result<Vec<IssueId>> {
pub async fn execute(&self, repo_id: &RepoId) -> Result<Vec<OpenIssue>> {
self.repo.get_issues(repo_id).await
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
use anyhow::{Ok, Result};
use std::ops::Deref;

use crate::application::{
commands::close_issue::CloseIssueCommand,
queries::{
get_issues::GetIssuesQuery,
get_repo::GetRepoQuery,
get_org::GetOrgQuery
},
};
use crate::domain::repository::{
IssueRepository,
OrgRepository
queries::{get_issues::GetIssuesQuery, get_org::GetOrgQuery, get_repo::GetRepoQuery},
};
use crate::domain::repository::{IssueRepository, OrgRepository};

pub async fn close_issues<R: OrgRepository, I: IssueRepository>(
get_org: GetOrgQuery<R>,
Expand All @@ -20,13 +14,18 @@ pub async fn close_issues<R: OrgRepository, I: IssueRepository>(
close_issue: CloseIssueCommand<I>,
owner: &str,
repo_name: &str,
keep_issue_id: Option<&str>,
) -> Result<()> {
let org_id = get_org.execute(owner).await?;
let repo_id = get_repo.execute(&org_id, &repo_name).await?;
let issues = get_issues.execute(&repo_id).await?;

for issue in issues.iter() {
close_issue.execute(issue).await?;
if keep_issue_id == Some(issue.id.deref()) {
continue;
}

close_issue.execute(&issue.id).await?;
}

Ok(())
Expand Down
15 changes: 15 additions & 0 deletions .github/scripts/dsm-launcher/src/domain/issue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ pub struct Issue {
pub assignees: Vec<Member>,
}

pub struct OpenIssue {
pub id: IssueId,
pub title: String,
}

impl OpenIssue {
pub fn new(id: String, title: String) -> Self {
OpenIssue {
id: IssueId::new(id),
title,
}
}
}

#[derive(Clone)]
pub struct IssueId(String);

impl IssueId {
Expand Down
10 changes: 7 additions & 3 deletions .github/scripts/dsm-launcher/src/domain/repository.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use async_trait::async_trait;
use anyhow::Result;
use async_trait::async_trait;

use super::{
issue::{Issue, IssueId, IssueType}, member::Member, org::OrgId, repo::RepoId, team::TeamId
issue::{Issue, IssueId, IssueType, OpenIssue},
member::Member,
org::OrgId,
repo::RepoId,
team::TeamId,
};

#[async_trait]
pub trait IssueRepository {
async fn get_issues(&self, repo: &RepoId) -> Result<Vec<IssueId>>;
async fn get_issues(&self, repo: &RepoId) -> Result<Vec<OpenIssue>>;
async fn get_issue_types(&self, repo: &RepoId) -> Result<Vec<IssueType>>;
async fn create_issue(&self, issue: Issue) -> Result<IssueId>;
async fn close_issue(&self, issue_id: &IssueId) -> Result<()>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,33 @@
use anyhow::{Ok, Result};
use async_trait::async_trait;
use anyhow::{Result, Ok};

use crate::domain::{
issue::{
Issue,
IssueId, IssueType
}, repo::RepoId, repository::IssueRepository
issue::{Issue, IssueId, IssueType, OpenIssue},
repo::RepoId,
repository::IssueRepository,
};
use crate::graphql_queries::{
close_issue::{
CloseIssue,
close_issue::Variables as CloseIssueVars
},
create_issue::{
CreateIssue,
create_issue::Variables as CreateIssueVars,
close_issue::{close_issue::Variables as CloseIssueVars, CloseIssue},
create_issue::{create_issue::Variables as CreateIssueVars, CreateIssue},
get_issue_types::{
get_issue_types::{GetIssueTypesNode, Variables as GetIssueTypesVars},
GetIssueTypes,
},
get_open_issues::{
GetOpenIssues,
get_open_issues::{
Variables as GetOpenIssuesVars,
GetOpenIssuesNode
},
get_open_issues::{GetOpenIssuesNode, Variables as GetOpenIssuesVars},
GetOpenIssues,
},
get_issue_types::{
GetIssueTypes,
get_issue_types::{
Variables as GetIssueTypesVars,
GetIssueTypesNode,
}
}
};

use crate::domain::errors::{
issue::IssueError,
issue_types::IssueTypesError,
repo::RepoError,
};
use crate::domain::errors::{issue::IssueError, issue_types::IssueTypesError, repo::RepoError};

use super::{
errors::GitHubAdapterError,
GitHubAdapter
};
use super::{errors::GitHubAdapterError, GitHubAdapter};

#[async_trait]
impl IssueRepository for GitHubAdapter {
async fn get_issues(&self, repo_id: &RepoId) -> Result<Vec<IssueId>> {
async fn get_issues(&self, repo_id: &RepoId) -> Result<Vec<OpenIssue>> {
let vars = GetOpenIssuesVars {
id: repo_id.to_string()
id: repo_id.to_string(),
};

let response = self.client.execute::<GetOpenIssues>(vars).await?;
Expand All @@ -63,18 +43,20 @@ impl IssueRepository for GitHubAdapter {
_ => {
return Err(GitHubAdapterError::UnexpectedNodeType.into());
}
}.issues.nodes;
}
.issues
.nodes;

Ok(issues
.ok_or(IssueError::IssuesWereNotFound)?
.into_iter()
.filter_map(|x| x.map(|issue| IssueId::new(issue.id)))
.collect::<Vec<IssueId>>())
.filter_map(|x| x.map(|issue| OpenIssue::new(issue.id, issue.title)))
.collect::<Vec<OpenIssue>>())
}

async fn get_issue_types(&self, repo_id: &RepoId) -> Result<Vec<IssueType>> {
let vars = GetIssueTypesVars {
id: repo_id.to_string()
id: repo_id.to_string(),
};

let response = self.client.execute::<GetIssueTypes>(vars).await?;
Expand All @@ -95,19 +77,14 @@ impl IssueRepository for GitHubAdapter {
.nodes
.ok_or(IssueTypesError::IssueTypesNotFound)?
.into_iter()
.filter_map(|x| {
x.map(|y| IssueType::new(y.id, y.name))
})
.filter_map(|x| x.map(|y| IssueType::new(y.id, y.name)))
.collect::<Vec<IssueType>>();

Ok(issue_types)
}

async fn create_issue(&self, issue: Issue) -> Result<IssueId> {
let logins: Vec<String> = issue.assignees
.iter()
.map(|x| x.id.to_string())
.collect();
let logins: Vec<String> = issue.assignees.iter().map(|x| x.id.to_string()).collect();

let vars = CreateIssueVars {
repo_id: issue.repo_id,
Expand All @@ -124,19 +101,18 @@ impl IssueRepository for GitHubAdapter {
}

let response_data = response.data.ok_or(IssueError::EmptyCreateIssueResponse)?;
let issue = response_data.create_issue.ok_or(IssueError::CreatedIssueNotFound)?;
let issue = response_data
.create_issue
.ok_or(IssueError::CreatedIssueNotFound)?;

Ok(IssueId::new(
issue
.issue
.ok_or(IssueError::CreatedIssueBodyNotFound)?
.id
issue.issue.ok_or(IssueError::CreatedIssueBodyNotFound)?.id,
))
}

async fn close_issue(&self, issue_id: &IssueId) -> Result<()> {
let vars = CloseIssueVars {
id: issue_id.to_string()
id: issue_id.to_string(),
};

let response = self.client.execute::<CloseIssue>(vars).await?;
Expand Down
93 changes: 55 additions & 38 deletions .github/scripts/dsm-launcher/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,90 +7,107 @@ use application::queries::get_org::GetOrgQuery;
use application::queries::get_repo::GetRepoQuery;
use application::queries::get_team::GetTeamQuery;
use application::queries::get_team_members::GetTeamMembersQuery;
use chrono::{FixedOffset, Utc};
use infrastructure::github_adapter::GitHubAdapter;
use std::fs;
use std::env;
use std::fs;
use std::rc::Rc;

mod graphql_queries {
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/graphql/mod.rs"));
}

mod application;
mod infrastructure;
mod domain;
mod infrastructure;

use application::use_cases::{close_issues::close_issues, create_issue::create_issue};
use infrastructure::github_graphql_client::GitHubGraphQLClient;
use application::use_cases::{
close_issues::close_issues,
create_issue::create_issue,
};

const DSM_TEAM_SLUG: &str = "DSM";
const DSM_TITLE_DATE_FORMAT: &str = "%a %b %d %Y";
const MOSCOW_UTC_OFFSET_SECONDS: i32 = 3 * 60 * 60;

#[tokio::main]
async fn main() -> Result<()> {
let repo_owner = env::var("GITHUB_REPO_OWNER")?;
let repo_name = env::var("GITHUB_REPO_NAME")?;
let team_slug = "DSM";
let body = fs::read_to_string("./.github/ISSUE_TEMPLATE/dsm.md")?;
let moscow_offset =
FixedOffset::east_opt(MOSCOW_UTC_OFFSET_SECONDS).expect("Moscow UTC offset must be valid");
let title: String = format!(
"[DSM] {}",
chrono::Utc::now().date_naive().format("%a %b %d %Y")
Utc::now()
.with_timezone(&moscow_offset)
.date_naive()
.format(DSM_TITLE_DATE_FORMAT)
);

let client = GitHubGraphQLClient::new(
"dsm-launcher".to_string(),
env::var("GITHUB_TOKEN")?,
)?;

let client = GitHubGraphQLClient::new("dsm-launcher".to_string(), env::var("GITHUB_TOKEN")?)?;

let adapter = Rc::new(GitHubAdapter::new(client.clone()));

let get_org = GetOrgQuery {
repo: adapter.clone()
repo: adapter.clone(),
};
let get_repo = GetRepoQuery {
repo: adapter.clone()
repo: adapter.clone(),
};
let get_issues = GetIssuesQuery {
repo: adapter.clone()
repo: adapter.clone(),
};
let get_issue_types = GetIssueTypes {
repo: adapter.clone()
repo: adapter.clone(),
};
let close_issue = CloseIssueCommand {
repo: adapter.clone()
repo: adapter.clone(),
};
let create_issue_ = CreateIssueCommand {
repo: adapter.clone()
repo: adapter.clone(),
};
let get_team = GetTeamQuery {
repo: adapter.clone()
};
let get_team_members = GetTeamMembersQuery {
repo: adapter
repo: adapter.clone(),
};
let get_team_members = GetTeamMembersQuery { repo: adapter };

let org_id = get_org.execute(&repo_owner).await?;
let repo_id = get_repo.execute(&org_id, &repo_name).await?;
let current_issue_id = get_issues
.clone()
.execute(&repo_id)
.await?
.into_iter()
.find(|issue| issue.title == title)
.map(|issue| issue.id.to_string());

close_issues(
get_org.clone(),
get_repo.clone(),
get_issues,
close_issue,
&repo_owner,
&repo_name
).await?;
create_issue(
get_org,
get_repo,
get_team,
get_team_members,
get_issue_types,
create_issue_,
&repo_owner,
&repo_name,
team_slug,
team_slug,
&title,
&body
).await?;
current_issue_id.as_deref(),
)
.await?;

if current_issue_id.is_none() {
create_issue(
get_org,
get_repo,
get_team,
get_team_members,
get_issue_types,
create_issue_,
&repo_owner,
&repo_name,
DSM_TEAM_SLUG,
DSM_TEAM_SLUG,
&title,
&body,
)
.await?;
}

Ok(())
}