Skip to content

Commit

Permalink
Refactor github module (#16)
Browse files Browse the repository at this point in the history
Signed-off-by: Sergio Castaño Arteaga <[email protected]>
  • Loading branch information
tegioz authored Sep 2, 2024
1 parent add3a26 commit 40e1fcd
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 188 deletions.
4 changes: 2 additions & 2 deletions dco2-aws-lambda/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Context;
use dco2::github::{self, GHClientOctorust};
use dco2::github::client::{AppConfig, GHClientOctorust};
use dco2_server::handlers::setup_router;
use figment::{providers::Env, Figment};
use lambda_http::{run, tracing, Error};
Expand All @@ -15,7 +15,7 @@ async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();

// Setup GitHub client
let cfg: github::AppConfig = Figment::new()
let cfg: AppConfig = Figment::new()
.merge(Env::prefixed("DCO2_"))
.extract()
.context("error setting up configuration")?;
Expand Down
2 changes: 1 addition & 1 deletion dco2-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This module defines some types to represent the server configuration.
use anyhow::Result;
use dco2::github::AppConfig;
use dco2::github::client::AppConfig;
use figment::{
providers::{Env, Format, Serialized, Yaml},
Figment,
Expand Down
7 changes: 5 additions & 2 deletions dco2-server/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use axum::{
};
use dco2::{
dco,
github::{DynGHClient, Event, EventError, EVENT_ID_HEADER, SIGNATURE_HEADER},
github::{
client::DynGHClient,
event::{Event, EventError, EVENT_ID_HEADER, EVENT_SIGNATURE_HEADER},
},
};
use hmac::{Hmac, Mac};
use sha2::Sha256;
Expand Down Expand Up @@ -91,7 +94,7 @@ async fn event(
#[allow(clippy::missing_errors_doc)]
pub fn verify_signature(secret: &[u8], headers: &HeaderMap, body: &[u8]) -> Result<()> {
if let Some(signature) = headers
.get(SIGNATURE_HEADER)
.get(EVENT_SIGNATURE_HEADER)
.and_then(|s| s.to_str().ok())
.and_then(|s| s.strip_prefix("sha256="))
.and_then(|s| hex::decode(s).ok())
Expand Down
2 changes: 1 addition & 1 deletion dco2-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use anyhow::{Context, Result};
use clap::Parser;
use config::{Config, LogFormat};
use dco2::github::GHClientOctorust;
use dco2::github::client::GHClientOctorust;
use dco2_server::handlers::setup_router;
use std::{path::PathBuf, sync::Arc};
use tokio::{net::TcpListener, signal};
Expand Down
6 changes: 3 additions & 3 deletions dco2/src/dco/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use crate::{
dco,
github::{
CheckRun, CheckRunAction, CheckRunConclusion, CheckRunEvent, CheckRunEventAction, CheckRunStatus,
Commit, DynGHClient, Event, PullRequestEvent, PullRequestEventAction,
client::{CheckRun, CheckRunAction, CheckRunConclusion, CheckRunStatus, Commit, DynGHClient},
event::{CheckRunEvent, CheckRunEventAction, Event, PullRequestEvent, PullRequestEventAction},
},
};
use anyhow::{Context, Result};
Expand Down Expand Up @@ -159,7 +159,7 @@ pub async fn process_pull_request_event(gh_client: DynGHClient, event: &PullRequ
CheckRunConclusion::ActionRequired,
vec![CheckRunAction {
label: "Set DCO to pass".to_string(),
description: "Override check result setting it to passed".to_string(),
description: "Manually set DCO check result to passed".to_string(),
identifier: ACTION_OVERRIDE.to_string(),
}],
)
Expand Down
5 changes: 2 additions & 3 deletions dco2/src/dco/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::{check, CheckInput};
use crate::{
dco::{CheckOutput, CommitCheckOutput, CommitError},
github::{Commit, GitUser},
dco::{check, CheckInput, CheckOutput, CommitCheckOutput, CommitError},
github::client::{Commit, GitUser},
};
use indoc::indoc;
use std::vec;
Expand Down
184 changes: 8 additions & 176 deletions dco2/src/github.rs → dco2/src/github/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,9 @@
use anyhow::Result;
use async_trait::async_trait;
use bytes::Bytes;
use chrono::{DateTime, Utc};
use http::HeaderMap;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use thiserror::Error;

/// Header representing the event unique identifier.
pub const EVENT_ID_HEADER: &str = "X-GitHub-Delivery";

/// Header representing the name of the event received.
pub const EVENT_NAME_HEADER: &str = "X-GitHub-Event";

/// Header representing the event payload signature.
pub const SIGNATURE_HEADER: &str = "X-Hub-Signature-256";

/// Abstraction layer over a GitHub client. This trait defines the methods that
/// a GHClient implementation must provide.
Expand Down Expand Up @@ -179,42 +167,6 @@ impl From<CheckRunConclusion> for octorust::types::ChecksCreateRequestConclusion
}
}

/// Check run event payload.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CheckRunEvent {
pub action: CheckRunEventAction,
pub check_run: CheckRunEventCheckRun,
pub installation: Installation,
pub repository: Repository,
pub requested_action: RequestedAction,
}

impl CheckRunEvent {
/// Get context information from event details.
pub fn ctx(&self) -> Ctx {
let (owner, repo) = split_full_name(&self.repository.full_name);
Ctx {
inst_id: self.installation.id,
owner: owner.to_string(),
repo: repo.to_string(),
}
}
}

/// Check run event action.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CheckRunEventAction {
RequestedAction,
Rerequested,
}

/// Check run event check run details.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CheckRunEventCheckRun {
pub head_sha: String,
}

/// Check run status.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
Expand All @@ -231,6 +183,14 @@ impl From<CheckRunStatus> for octorust::types::JobStatus {
}
}

/// Information about the target of a GitHub API request.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Ctx {
pub inst_id: i64,
pub owner: String,
pub repo: String,
}

/// Commit information.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Commit {
Expand Down Expand Up @@ -264,138 +224,10 @@ impl From<octorust::types::CommitDataType> for Commit {
}
}

/// Information about the target of a GitHub API request.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Ctx {
pub inst_id: i64,
pub owner: String,
pub repo: String,
}

/// Webhook event.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Event {
CheckRun(CheckRunEvent),
PullRequest(PullRequestEvent),
}

impl TryFrom<(&HeaderMap, &Bytes)> for Event {
type Error = EventError;

/// Try to create a new event instance from the provided headers and body.
fn try_from((headers, body): (&HeaderMap, &Bytes)) -> Result<Self, Self::Error> {
match headers.get(EVENT_NAME_HEADER) {
Some(event_name) => match event_name.as_bytes() {
b"check_run" => {
let event = serde_json::from_slice(body).map_err(|_| EventError::InvalidPayload)?;
Ok(Event::CheckRun(event))
}
b"pull_request" => {
let event = serde_json::from_slice(body).map_err(|_| EventError::InvalidPayload)?;
Ok(Event::PullRequest(event))
}
_ => Err(EventError::UnsupportedEvent),
},
None => Err(EventError::MissingHeader),
}
}
}

/// Errors that may occur while creating a new event instance.
#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum EventError {
#[error("invalid payload")]
InvalidPayload,
#[error("event header missing")]
MissingHeader,
#[error("unsupported event")]
UnsupportedEvent,
}

/// Git user information.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct GitUser {
pub name: String,
pub email: String,
pub is_bot: bool,
}

/// GitHub application installation information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Installation {
pub id: i64,
}

/// Pull request information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PullRequest {
pub base: PullRequestBase,
pub head: PullRequestHead,
pub html_url: String,
}

/// Pull request base information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PullRequestBase {
#[serde(rename = "ref")]
pub ref_: String,
pub sha: String,
}

/// Pull request event payload.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PullRequestEvent {
pub action: PullRequestEventAction,
pub installation: Installation,
pub pull_request: PullRequest,
pub repository: Repository,
}

impl PullRequestEvent {
/// Get context information from event details.
pub fn ctx(&self) -> Ctx {
let (owner, repo) = split_full_name(&self.repository.full_name);
Ctx {
inst_id: self.installation.id,
owner: owner.to_string(),
repo: repo.to_string(),
}
}
}

/// Pull request event action.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PullRequestEventAction {
Opened,
Synchronize,
#[serde(other)]
Other,
}

/// Pull request head information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PullRequestHead {
#[serde(rename = "ref")]
pub ref_: String,
pub sha: String,
}

/// Repository information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Repository {
pub full_name: String,
}

/// Requested action information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RequestedAction {
pub identifier: String,
}

/// Helper function that splits a repository's full name and returns the owner
/// and the repo name as a tuple.
fn split_full_name(full_name: &str) -> (&str, &str) {
let mut parts = full_name.split('/');
(parts.next().unwrap(), parts.next().unwrap())
}
Loading

0 comments on commit 40e1fcd

Please sign in to comment.