// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Web hook structures
//!
//! These hooks are received from Gitlab when registered as a web hook within a project.
//!
//! Gitlab does not have consistent structures for its hooks, so they often change from
//! version to version.
use chrono::{DateTime, NaiveDate, NaiveDateTime, TimeZone, Utc};
use log::error;
use serde::de::{Error, Unexpected};
use serde::{Deserialize, Deserializer};
use serde_json::{self, Value};
/// A wrapper struct for dates in web hooks.
///
/// Gitlab does not use a standard date format for dates in web hooks. This structure supports
/// deserializing the formats that have been observed.
#[derive(Debug, Clone, Copy)]
pub struct HookDate(DateTime<Utc>);
impl<'de> Deserialize<'de> for HookDate {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let val = String::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&val, "%Y-%m-%d %H:%M:%S UTC")
// XXX(chrono-0.4.25): `dt.and_utc()`
.map(|dt| Utc.from_utc_datetime(&dt))
.or_else(|_| DateTime::parse_from_rfc3339(&val).map(|dt| dt.with_timezone(&Utc)))
.or_else(|_| {
DateTime::parse_from_str(&val, "%Y-%m-%d %H:%M:%S %z")
.map(|dt| dt.with_timezone(&Utc))
})
.map_err(|err| {
D::Error::invalid_value(
Unexpected::Other("hook date"),
&format!("Unsupported format: {} {:?}", val, err).as_str(),
)
})
.map(HookDate)
}
}
impl AsRef<DateTime<Utc>> for HookDate {
fn as_ref(&self) -> &DateTime<Utc> {
&self.0
}
}
/// Project information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct ProjectHookAttrs {
/// The display name of the project.
pub name: String,
/// The description of the project.
pub description: Option<String>,
/// The URL for the project's homepage.
pub web_url: String,
/// The URL to the project avatar.
pub avatar_url: Option<String>,
/// The URL to clone the repository over SSH.
pub git_ssh_url: String,
/// The URL to clone the repository over HTTPS.
pub git_http_url: String,
/// The namespace the project lives in.
pub namespace: String,
/// Integral value for the project's visibility.
pub visibility_level: u64,
/// The path to the project's repository with its namespace.
pub path_with_namespace: String,
/// The default branch for the project.
pub default_branch: Option<String>,
}
/// Wiki project information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct ProjectWikiHookAttrs {
/// The URL for the project's homepage.
pub web_url: String,
/// The URL to clone the repository over SSH.
pub git_ssh_url: String,
/// The URL to clone the repository over HTTPS.
pub git_http_url: String,
/// The path to the project's repository with its namespace.
pub path_with_namespace: String,
/// The default branch for the project.
pub default_branch: String,
}
/// User information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct UserHookAttrs {
/// The name of the user.
pub name: String,
/// The handle of the user.
pub username: String,
/// The URL to the avatar of the user.
pub avatar_url: Option<String>,
/// The email address of the user.
pub email: Option<String>,
}
/// The identity of a user exposed through a hook.
#[derive(Deserialize, Debug, Clone)]
pub struct HookCommitIdentity {
/// The name of the author or committer.
pub name: String,
/// The email address of the author or committer.
pub email: String,
}
/// Commit information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct CommitHookAttrs {
/// The commit's ID.
pub id: String,
/// The commit message.
pub message: String,
pub timestamp: DateTime<Utc>,
/// The URL of the commit.
pub url: String,
/// The author of the commit.
pub author: HookCommitIdentity,
pub added: Option<Vec<String>>,
pub modified: Option<Vec<String>>,
pub removed: Option<Vec<String>>,
}
/// A push hook.
#[derive(Deserialize, Debug, Clone)]
pub struct PushHook {
/// The event which occurred.
pub object_kind: String,
/// The old object ID of the ref before the push.
pub before: String,
/// The new object ID of the ref after the push.
pub after: String,
#[serde(rename = "ref")]
/// The name of the reference which has been pushed.
pub ref_: String,
/// The new object ID of the ref after the push.
pub checkout_sha: Option<String>,
/// The message for the push (used for annotated tags).
pub message: Option<String>,
/// The ID of the user who pushed.
pub user_id: u64,
/// The name of the user who pushed.
pub user_name: String,
/// The username of the user who pushed.
pub user_username: String,
/// The email address of the user who pushed.
pub user_email: Option<String>,
/// The URL of the user's avatar.
pub user_avatar: Option<String>,
/// The ID of the project pushed to.
pub project_id: u64,
/// Attributes of the project.
pub project: ProjectHookAttrs,
/// The commits pushed to the repository.
///
/// Limited to 20 commits.
pub commits: Vec<CommitHookAttrs>, // limited to 20 commits
/// The total number of commits pushed.
pub total_commits_count: u64,
}
/// Actions which may occur on an issue.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum IssueAction {
/// The issue was updated.
#[serde(rename = "update")]
Update,
/// The issue was opened.
#[serde(rename = "open")]
Open,
/// The issue was closed.
#[serde(rename = "close")]
Close,
/// The issue was reopened.
#[serde(rename = "reopen")]
Reopen,
}
/// The states an issue may be in.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum IssueState {
/// The issue is open.
#[serde(rename = "opened")]
Opened,
/// The issue has been closed.
#[serde(rename = "closed")]
Closed,
/// The issue has been opened after being closed.
#[serde(rename = "reopened")]
Reopened,
}
/// Issue information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct IssueHookAttrs {
/// The ID of the issue.
pub id: u64,
/// The title of the issue.
pub title: String,
/// The ID of the assignee of the issue.
pub assignee_id: Option<u64>,
/// The ID of the author of the issue.
pub author_id: u64,
/// The ID of the project.
pub project_id: u64,
/// When the issue was created.
pub created_at: HookDate,
/// When the issue was last updated.
pub updated_at: HookDate,
/// When the issue was deleted.
pub deleted_at: Option<HookDate>,
/// When the issue was closed.
pub closed_at: Option<HookDate>,
/// When the issue is due.
pub due_date: Option<NaiveDate>,
/// The ID of the user which last updated the issue.
pub updated_by_id: Option<u64>,
pub moved_to_id: Option<Value>, // ???
/// The branch name for the issue.
pub branch_name: Option<String>,
/// The description of the issue.
pub description: Option<String>,
/// The ID of the milestone of the issue.
pub milestone_id: Option<u64>,
/// The state of the issue.
pub state: IssueState,
/// The user-visible ID of the issue.
pub iid: u64,
/// Whether the issue is confidential or not.
pub confidential: bool,
/// The time estimate, in seconds.
pub time_estimate: u64,
/// The total time spent, in seconds.
pub total_time_spent: u64,
/// The time estimate, as a human-readable string.
pub human_time_estimate: Option<String>,
/// The total time spent, as a human-readable string.
pub human_total_time_spent: Option<String>,
// It seems that notes miss these properties?
/// The URL of the issue.
pub url: Option<String>,
/// The type of action which caused the hook.
pub action: Option<IssueAction>,
}
/// An issue hook.
#[derive(Deserialize, Debug, Clone)]
pub struct IssueHook {
/// The event which occurred.
pub object_kind: String,
/// The user which triggered the hook.
pub user: UserHookAttrs,
/// The project the hook was created for.
pub project: ProjectHookAttrs,
/// Attributes of the issue.
pub object_attributes: IssueHookAttrs,
/// The assignee of the issue.
pub assignee: Option<UserHookAttrs>,
}
/// Actions which may occur on a merge request.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeRequestAction {
/// The merge request was updated.
#[serde(rename = "update")]
Update,
/// The merge request was opened.
#[serde(rename = "open")]
Open,
/// The merge request was closed.
#[serde(rename = "close")]
Close,
/// The merge request was reopened.
#[serde(rename = "reopen")]
Reopen,
/// The merge request was approved.
#[serde(rename = "approved")]
Approved,
/// A merge request approval was revoked.
#[serde(rename = "unapproved")]
Unapproved,
/// A merge request approval has been added
#[serde(rename = "approval")]
Approval,
/// A merge request approval has been removed
#[serde(rename = "unapproval")]
Unapproval,
/// The merge request was merged.
#[serde(rename = "merge")]
Merge,
}
/// Merge parameters for a merge request.
#[derive(Deserialize, Debug, Clone)]
pub struct MergeRequestParams {
force_remove_source_branch: Option<Value>, // sigh
}
impl MergeRequestParams {
/// Whether the author of the merge request indicated that the source branch should be deleted
/// upon merge or not.
// https://gitlab.com/gitlab-org/gitlab-ce/issues/20880
pub fn force_remove_source_branch(&self) -> bool {
self.force_remove_source_branch
.as_ref()
.map_or(false, |val| {
if let Some(as_str) = val.as_str() {
as_str == "1"
} else if let Some(as_bool) = val.as_bool() {
as_bool
} else {
error!(
target: "gitlab",
"unknown value for force_remove_source_branch: {}",
val,
);
false
}
})
}
}
/// The states a merge request may be in.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeRequestState {
/// The merge request is open.
#[serde(rename = "opened")]
Opened,
/// The merge request has been closed before merging.
#[serde(rename = "closed")]
Closed,
/// The merge request has been opened after closing.
#[serde(rename = "reopened")]
Reopened,
/// The merge request has been merged.
#[serde(rename = "merged")]
Merged,
/// The merge request is locked from further discussion or updates.
#[serde(rename = "locked")]
Locked,
}
/// The status of the possible merge for a merge request.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeStatus {
/// The merge request has just been created.
#[serde(rename = "preparing")]
Preparing,
/// The merge request has not been checked yet.
#[serde(rename = "unchecked")]
Unchecked,
/// The merge request is currently being checked.
#[serde(rename = "checking")]
Checking,
/// The merge request may be merged.
#[serde(rename = "can_be_merged")]
CanBeMerged,
/// The merge request may not be merged yet.
#[serde(rename = "cannot_be_merged")]
CannotBeMerged,
/// The merge request has not been checked but previously could not be merged.
#[serde(rename = "cannot_be_merged_recheck")]
CannotBeMergedRecheck,
/// The merge request could not be merged previously, but is being rechecked.
#[serde(rename = "cannot_be_merged_rechecking")]
CannotBeMergedRechecking,
}
/// Merge request information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct MergeRequestHookAttrs {
/// The source project of the merge request.
///
/// If this is `None`, the source repository has been deleted.
pub source: Option<ProjectHookAttrs>,
/// The target project of the merge request.
pub target: ProjectHookAttrs,
pub last_commit: Option<CommitHookAttrs>,
/// Whether the merge request is a draft or not.
pub draft: bool,
/// Whether the merge request is a work-in-progress or not.
#[deprecated(since = "0.1601.0", note = "Use the 'draft' member instead.")]
pub work_in_progress: bool,
/// The object ID of the merge commit which is currently being handled.
pub in_progress_merge_commit_sha: Option<String>,
/// The ID of the merge request.
pub id: u64,
/// The target branch of the merge request.
pub target_branch: String,
/// The ID of the target project.
pub target_project_id: u64,
/// The source branch of the merge request.
pub source_branch: String,
/// The ID of the source project.
pub source_project_id: Option<u64>,
/// The ID of the author of the merge request.
pub author_id: u64,
/// The ID of the assignee of the merge request.
pub assignee_id: Option<u64>,
/// The title of the merge request.
pub title: String,
/// When the merge request was created.
pub created_at: HookDate,
/// When the merge request was last updated.
pub updated_at: HookDate,
/// When the merge request was deleted.
pub deleted_at: Option<HookDate>,
/// When the merge request was locked.
pub locked_at: Option<HookDate>,
/// The ID of the user which last updated the merge request.
pub updated_by_id: Option<u64>,
/// The object ID of the commit which merged the merge request.
pub merge_commit_sha: Option<String>,
pub merge_error: Option<Value>, // String?
/// The parameters for merging the merge request.
pub merge_params: MergeRequestParams,
/// The user which merged the merge request.
pub merge_user_id: Option<u64>,
/// Whether the merge request will be merged once all builds succeed or not.
pub merge_when_pipeline_succeeds: bool,
// st_commits
// st_diffs
/// The milestone of the merge request.
pub milestone_id: Option<u64>,
pub oldrev: Option<String>,
/// The state of the merge request.
pub state: MergeRequestState,
/// The merge status of the merge request.
pub merge_status: MergeStatus,
/// The user-visible ID of the merge request.
pub iid: u64,
/// The description of the merge request.
pub description: Option<String>,
/// The newest pipeline, if any
pub head_pipeline_id: Option<u64>,
// It seems that notes miss these properties?
/// The URL of the merge request.
pub url: Option<String>,
/// The type of action which caused the hook.
pub action: Option<MergeRequestAction>,
pub time_estimate: u64,
}
/// A merge request hook.
#[derive(Deserialize, Debug, Clone)]
pub struct MergeRequestHook {
/// The event which occurred.
pub object_kind: String,
/// The user which triggered the hook.
pub user: UserHookAttrs,
/// The project the hook was created for.
pub project: ProjectHookAttrs,
/// Attributes of the merge request.
pub object_attributes: MergeRequestHookAttrs,
/// The assignee of the merge request.
pub assignee: Option<UserHookAttrs>,
/// Details about the changes on the MR
pub changes: Option<MergeRequestChanges>,
}
/// MR Changes
#[derive(Deserialize, Debug, Clone)]
pub struct MergeRequestChanges {
pub title: Option<MergeRequestChange<String>>,
pub updated_at: Option<MergeRequestChange<String>>,
pub merge_status: Option<MergeRequestChange<String>>,
pub merge_commit_sha: Option<MergeRequestChange<Option<String>>>,
}
/// A MR change
#[derive(Deserialize, Debug, Clone)]
pub struct MergeRequestChange<T> {
pub previous: T,
pub current: T,
}
/// The type of a snippet.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum SnippetType {
/// A project-owned snippet.
#[serde(rename = "ProjectSnippet")]
Project,
/// A user-owned snippet.
#[serde(rename = "PersonalSnippet")]
Personal,
}
/// Snippet information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct SnippetHookAttrs {
/// The title of the snippet.
pub title: String,
/// The content of the snippet.
pub content: String,
/// The author of the snippet.
pub author_id: u64,
/// The project the snippet belongs to.
pub project_id: Option<u64>,
/// When the snippet was created.
pub created_at: HookDate,
/// When the snippet was last updated.
pub updated_at: HookDate,
/// The name of the snippet.
pub file_name: String,
#[serde(rename = "type")]
/// The type of the snippet.
pub type_: SnippetType,
/// The visibility of the snippet.
pub visibility_level: u64,
}
/// Actions which may occur on a wiki page.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum WikiPageAction {
/// A wiki page was created.
#[serde(rename = "create")]
Create,
/// A wiki page was updated.
#[serde(rename = "update")]
Update,
}
/// Wiki information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct WikiPageHookAttrs {
/// The title of the wiki page.
pub title: String,
/// The content of the wiki page.
pub content: String,
pub format: String,
pub message: String,
/// The slug of the wiki page.
pub slug: String,
/// The URL of the wiki page.
pub url: String,
/// The type of action which caused the hook.
pub action: WikiPageAction,
}
/// Diff information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct DiffHookAttrs {
pub diff: String,
/// The path on the new side of the diff.
pub new_path: String,
/// The path on the old side of the diff.
pub old_path: String,
/// The mode on the old side of the diff.
// TODO: Create a mode type.
pub a_mode: String,
/// The mode on the new side of the diff.
pub b_mode: String,
/// Whether the diff indicates the addition of a file.
pub new_file: bool,
/// Whether the diff indicates the rename of a file.
pub renamed_file: bool,
/// Whether the diff indicates the deletion of a file.
pub deleted_file: bool,
pub too_large: bool,
}
#[derive(Deserialize, Debug, Clone)]
// FIXME(gitlab#21467): This can apparently be a string sometimes.
// https://gitlab.com/gitlab-org/gitlab-ce/issues/21467
pub struct PositionHookAttrs {
pub base_sha: String,
pub head_sha: String,
pub start_sha: String,
pub old_line: Option<u64>,
/// The path on the old side of the diff.
pub old_path: String,
pub new_line: Option<u64>,
/// The path on the new side of the diff.
pub new_path: String,
}
/// The entities a note may be added to.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum NoteType {
/// A note on a commit.
Commit,
/// A note on an issue.
Issue,
/// A note on a merge request.
MergeRequest,
/// A note on a snippet.
Snippet,
}
/// The ID of an entity a note is attached to.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NoteableId {
/// The ID of the commit for a commit note.
Commit(String),
/// The ID of the issue for an issue note.
Issue(u64),
/// The ID of the merge request for a merge request note.
MergeRequest(u64),
/// The ID of the snippet for a snippet note.
Snippet(u64),
}
/// Note (comment) information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct NoteHookAttrs {
/// The ID of the note.
pub id: u64,
/// The content of the note.
pub note: String,
/// The type of entity the note is attached to.
pub noteable_type: NoteType,
// pub original_position: Option<PositionHookAttrs>,
// pub position: Option<PositionHookAttrs>,
/// The author of the note.
pub author_id: u64,
/// When the note was created.
pub created_at: HookDate,
/// When the note was last updated.
pub updated_at: HookDate,
/// The ID of the user who last updated the note.
pub updated_by_id: Option<u64>,
/// When the note was marked as resolved.
pub resolved_at: Option<HookDate>,
/// The ID of the user who marked the note as resolved.
pub resolved_by_id: Option<u64>,
/// The ID of the project.
pub project_id: u64,
/// The URL of an attachment to the note.
pub attachment: Option<String>,
pub line_code: Option<String>, // XXX: This is some internal format.
pub commit_id: Option<String>, // XXX(8.11): apparently can be an empty string?
pub discussion_id: String,
pub original_discussion_id: Option<String>,
noteable_id: Value, // Keep as JSON because its type depends on what `noteable_type` is.
/// Whether the note was created by a user or in response to an external action.
pub system: bool,
pub st_diff: Option<DiffHookAttrs>,
/// The URL of the note.
pub url: String,
// XXX: What is this field?
#[serde(rename = "type")]
pub type_: Option<String>,
// XXX: Seems to have been removed?
// pub is_award: bool,
}
impl NoteHookAttrs {
/// The ID of the object the note is for.
pub fn noteable_id(&self) -> Option<NoteableId> {
match self.noteable_type {
NoteType::Commit => {
self.noteable_id
.as_str()
.map(|id| NoteableId::Commit(id.into()))
},
NoteType::Issue => self.noteable_id.as_u64().map(NoteableId::Issue),
NoteType::MergeRequest => self.noteable_id.as_u64().map(NoteableId::MergeRequest),
NoteType::Snippet => self.noteable_id.as_u64().map(NoteableId::Snippet),
}
}
}
/// A note hook.
#[derive(Deserialize, Debug, Clone)]
pub struct NoteHook {
/// The event which occurred.
pub object_kind: String,
/// The user who triggered the hook.
pub user: UserHookAttrs,
/// The ID of the project the note belongs to.
pub project_id: u64,
/// The project the note belongs to.
pub project: ProjectHookAttrs,
/// The attributes on the note itself.
pub object_attributes: NoteHookAttrs,
/// The commit the note is associated with (for commit notes).
pub commit: Option<CommitHookAttrs>,
/// The issue the note is associated with (for issue notes).
pub issue: Option<IssueHookAttrs>,
/// The merge request the note is associated with (for merge request notes).
pub merge_request: Option<MergeRequestHookAttrs>,
/// The snippet the note is associated with (for snippet notes).
pub snippet: Option<SnippetHookAttrs>,
}
/// Build user information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct BuildUserHookAttrs {
/// The ID of the user.
pub id: Option<u64>,
/// The user's name.
pub name: Option<String>,
/// The user's email address.
pub email: Option<String>,
}
/// Build commit information exposed in hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct BuildCommitHookAttrs {
pub id: String,
/// The object ID of the commit.
pub sha: String,
/// The full commit message.
pub message: String,
/// The commit's author's name.
pub author_name: String,
/// The commit's author's email address.
pub author_email: String,
pub status: String,
pub duration: u64,
/// When the build started.
pub started_at: Option<HookDate>,
/// When the build completed.
pub finished_at: Option<HookDate>,
}
/// Project information exposed in build hooks.
#[derive(Deserialize, Debug, Clone)]
pub struct BuildProjectHookAttrs {
/// The display name of the project.
pub name: String,
/// The description of the project.
pub description: Option<String>,
/// The URL for the project's homepage.
pub homepage: String,
/// The URL to clone the repository over HTTPS.
pub git_http_url: String,
/// The URL to clone the repository over SSH.
pub git_ssh_url: String,
/// Integral value for the project's visibility.
pub visibility_level: u64,
}
/// A build hook.
#[derive(Deserialize, Debug, Clone)]
pub struct BuildHook {
/// The event which occurred.
pub object_kind: String,
#[serde(rename = "ref")]
/// The name of the reference that was tested.
pub ref_: String,
pub tag: String,
pub before_sha: String,
/// The object ID that was built.
pub sha: String,
/// The ID of the build.
pub build_id: u64,
/// The name of the build.
pub build_name: String,
pub build_stage: String,
/// When the build started.
pub build_started_at: Option<HookDate>,
/// When the build completed.
pub build_finished_at: Option<HookDate>,
pub build_duration: Option<u64>,
/// Whether the build is allowed to fail.
pub build_allow_failure: bool,
/// The ID of the project.
pub project_id: u64,
/// The user which owns the build.
pub user: BuildUserHookAttrs,
/// The commit which was built.
pub commit: BuildCommitHookAttrs,
/// The repository the build is for.
pub repository: BuildProjectHookAttrs,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PipelineVariable {
/// Environment variable key
pub key: String,
/// Environment variable value
pub value: String,
}
/// States for commit statuses.
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatusState {
/// The check was created.
#[serde(rename = "created")]
Created,
/// The check is waiting for some other resource.
#[serde(rename = "waiting_for_resource")]
WaitingForResource,
/// The check is currently being prepared.
#[serde(rename = "preparing")]
Preparing,
/// The check is queued.
#[serde(rename = "pending")]
Pending,
/// The check is currently running.
#[serde(rename = "running")]
Running,
/// The check succeeded.
#[serde(rename = "success")]
Success,
/// The check failed.
#[serde(rename = "failed")]
Failed,
/// The check was canceled.
#[serde(rename = "canceled")]
Canceled,
/// The check was skipped.
#[serde(rename = "skipped")]
Skipped,
/// The check is waiting for manual action.
#[serde(rename = "manual")]
Manual,
/// The check is scheduled to run at some point in time.
#[serde(rename = "scheduled")]
Scheduled,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PipelineHookAttrs {
pub id: u64,
/// The object ID that was tested.
pub sha: String,
#[serde(rename = "ref")]
/// The name of the reference that was tested.
pub ref_: Option<String>,
/// The status of the pipeline.
pub status: StatusState,
pub before_sha: String,
/// Was this pipeline triggered by a tag.
pub tag: bool,
/// When the pipeline was created.
pub created_at: HookDate,
/// When the pipeline completed.
pub finished_at: Option<HookDate>,
/// Duration of pipeline in seconds.
pub duration: Option<u64>,
/// What triggered the pipeline.
pub source: String,
/// The stages of the pipeline.
pub stages: Vec<String>,
/// Environment variables manually set by the user starting the pipeline.
pub variables: Vec<PipelineVariable>,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PipelineBuildRunner {
/// The runner id.
pub id: u64,
/// The runner description
pub description: String,
/// Whether the runner is active.
pub active: bool,
/// Whether the runner is shared.
pub is_shared: bool,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PipelineMergeRequestAttrs {
pub id: u64,
pub iid: u64,
/// The title of the merge request.
pub title: String,
/// The target branch of the merge request.
pub target_branch: String,
/// The ID of the target project.
pub target_project_id: u64,
/// The source branch of the merge request.
pub source_branch: String,
/// The ID of the source project.
pub source_project_id: Option<u64>,
pub state: MergeRequestState,
pub merge_status: MergeStatus,
pub url: String,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PipelineProjectAttrs {
pub id: u64,
/// The display name of the project.
pub name: String,
/// The description of the project.
pub description: Option<String>,
/// The URL for the project's homepage.
pub web_url: String,
/// The URL to the project avatar.
pub avatar_url: Option<String>,
/// The URL to clone the repository over SSH.
pub git_ssh_url: String,
/// The URL to clone the repository over HTTPS.
pub git_http_url: String,
/// The namespace the project lives in.
pub namespace: String,
/// Integral value for the project's visibility.
pub visibility_level: u64,
/// The path to the project's repository with its namespace.
pub path_with_namespace: String,
/// The default branch for the project.
pub default_branch: Option<String>,
/// The path to the ci config file.
pub ci_config_path: Option<String>,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PipelineHook {
/// The event which occured.
pub object_kind: String,
/// The pipeline.
pub object_attributes: PipelineHookAttrs,
/// The merge request this pipeline is running for.
pub merge_request: Option<PipelineMergeRequestAttrs>,
/// The user that started the the pipeline.
pub user: UserHookAttrs,
/// The project this pipeline is running in.
pub project: PipelineProjectAttrs,
/// The commit this pipeline is running for
pub commit: Option<CommitHookAttrs>,
}
/// A wiki page hook.
#[derive(Deserialize, Debug, Clone)]
pub struct WikiPageHook {
/// The event which occurred.
pub object_kind: String,
/// The user who caused the hook.
pub user: UserHookAttrs,
/// The project the wiki belongs to.
pub project: ProjectHookAttrs,
/// The wiki the page belongs to.
pub wiki: ProjectWikiHookAttrs,
/// The wiki page.
pub object_attributes: WikiPageHookAttrs,
}
/// A deserializable structure for all Gitlab web hooks.
#[derive(Debug, Clone)]
pub enum WebHook {
/// A push hook.
Push(Box<PushHook>),
/// An issue hook.
Issue(Box<IssueHook>),
/// A merge request hook.
MergeRequest(Box<MergeRequestHook>),
/// A note hook.
Note(Box<NoteHook>),
/// A build hook.
Build(Box<BuildHook>),
/// A pipeline hook.
Pipeline(Box<PipelineHook>),
/// A wiki page hook.
WikiPage(Box<WikiPageHook>),
}
impl<'de> Deserialize<'de> for WebHook {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let val = <Value as Deserialize>::deserialize(deserializer)?;
let object_kind = match val.pointer("/object_kind") {
Some(Value::String(kind)) => kind,
Some(_) => {
return Err(D::Error::invalid_type(
Unexpected::Other("JSON value"),
&"a string",
));
},
None => {
return Err(D::Error::missing_field("object_kind"));
},
};
let hook_res = match object_kind.as_ref() {
"push" | "tag_push" => {
serde_json::from_value(val).map(|hook| WebHook::Push(Box::new(hook)))
},
"issue" => serde_json::from_value(val).map(|hook| WebHook::Issue(Box::new(hook))),
"merge_request" => {
serde_json::from_value(val).map(|hook| WebHook::MergeRequest(Box::new(hook)))
},
"note" => serde_json::from_value(val).map(|hook| WebHook::Note(Box::new(hook))),
"build" => serde_json::from_value(val).map(|hook| WebHook::Build(Box::new(hook))),
"pipeline" => serde_json::from_value(val).map(|hook| WebHook::Pipeline(Box::new(hook))),
_ => {
return Err(D::Error::invalid_value(
Unexpected::Other("object kind"),
&format!("unrecognized webhook object kind: {}", object_kind).as_str(),
));
},
};
hook_res.map_err(|err| {
D::Error::invalid_value(
Unexpected::Other("web hook"),
&format!("{:?}", err).as_str(),
)
})
}
}