diff --git a/data/pipeline.json b/data/pipeline.json new file mode 100644 index 0000000000000000000000000000000000000000..3cfab6de839a7ca9a7bf79d0e2671e0daa63e6f8 --- /dev/null +++ b/data/pipeline.json @@ -0,0 +1,157 @@ +{ + "builds": [ + { + "artifacts_file": { + "filename": "artifacts.zip", + "size": 57540037 + }, + "created_at": "2018-12-28 01:42:47 UTC", + "finished_at": "2018-12-28 02:03:51 UTC", + "id": 139419296, + "manual": false, + "name": "package-deb-armv7-build", + "runner": { + "active": true, + "description": "shared-runners-manager-6.gitlab.com", + "id": 380987, + "is_shared": true + }, + "stage": "build", + "started_at": "2018-12-28 01:42:47 UTC", + "status": "success", + "user": { + "avatar_url": "https:\/\/secure.gravatar.com\/avatar\/a871331a2dfc6a0711e397a3ad9abe53?s=80&d=identicon", + "name": "noyez", + "username": "noyez" + }, + "when": "on_success" + }, + { + "artifacts_file": { + "filename": "artifacts.zip", + "size": 57563165 + }, + "created_at": "2018-12-28 00:52:52 UTC", + "finished_at": "2018-12-28 01:25:40 UTC", + "id": 139413492, + "manual": false, + "name": "package-deb-armv7-build", + "runner": { + "active": true, + "description": "shared-runners-manager-3.gitlab.com", + "id": 44028, + "is_shared": true + }, + "stage": "build", + "started_at": "2018-12-28 00:52:53 UTC", + "status": "success", + "user": { + "avatar_url": "https:\/\/secure.gravatar.com\/avatar\/a871331a2dfc6a0711e397a3ad9abe53?s=80&d=identicon", + "name": "noyez", + "username": "noyez" + }, + "when": "on_success" + }, + { + "artifacts_file": { + "filename": "artifacts.zip", + "size": 57574697 + }, + "created_at": "2018-12-22 02:47:17 UTC", + "finished_at": "2018-12-22 03:20:10 UTC", + "id": 138014345, + "manual": false, + "name": "package-deb-armv7-build", + "runner": { + "active": true, + "description": "shared-runners-manager-3.gitlab.com", + "id": 44028, + "is_shared": true + }, + "stage": "build", + "started_at": "2018-12-22 03:00:11 UTC", + "status": "success", + "user": { + "avatar_url": "https:\/\/secure.gravatar.com\/avatar\/a871331a2dfc6a0711e397a3ad9abe53?s=80&d=identicon", + "name": "noyez", + "username": "noyez" + }, + "when": "on_success" + }, + { + "artifacts_file": { + "filename": "as", + "size": 15279 + }, + "created_at": "2018-12-22 02:47:17 UTC", + "finished_at": "2018-12-22 03:00:09 UTC", + "id": 138014344, + "manual": false, + "name": "test-native", + "runner": { + "active": true, + "description": "shared-runners-manager-5.gitlab.com", + "id": 380986, + "is_shared": true + }, + "stage": "test", + "started_at": "2018-12-22 02:47:17 UTC", + "status": "success", + "user": { + "avatar_url": "https:\/\/secure.gravatar.com\/avatar\/a871331a2dfc6a0711e397a3ad9abe53?s=80&d=identicon", + "name": "noyez", + "username": "noyez" + }, + "when": "on_success" + } + ], + "commit": { + "author": { + "email": "brad@bkn.lan", + "name": "noyez" + }, + "id": "e16784d8462ad3ee0eee0e27b5c195a5fa375154", + "message": "version bump\n", + "timestamp": "2018-12-22T02:47:10Z", + "url": "https:\/\/gitlab.com\/Embue\/hmm-core\/commit\/e16784d8462ad3ee0eee0e27b5c195a5fa375154" + }, + "object_attributes": { + "before_sha": "49a2ea8927c50664c2bcd133485b64d04526b367", + "created_at": "2018-12-22 02:47:17 UTC", + "detailed_status": "passed", + "duration": 2035, + "finished_at": "2018-12-28 02:03:51 UTC", + "id": 41064864, + "ref": "bkn_streamid_addition", + "sha": "e16784d8462ad3ee0eee0e27b5c195a5fa375154", + "stages": [ + "test", + "build" + ], + "status": "success", + "tag": false, + "variables": [ + + ] + }, + "object_kind": "pipeline", + "project": { + "avatar_url": null, + "ci_config_path": null, + "default_branch": "master", + "description": "", + "git_http_url": "https:\/\/gitlab.com\/Embue\/hmm-core.git", + "git_ssh_url": "git@gitlab.com:Embue\/hmm-core.git", + "id": 6529838, + "name": "hmm-core", + "namespace": "Embue", + "path_with_namespace": "Embue\/hmm-core", + "visibility_level": 0, + "web_url": "https:\/\/gitlab.com\/Embue\/hmm-core" + }, + "user": { + "avatar_url": "https:\/\/secure.gravatar.com\/avatar\/a871331a2dfc6a0711e397a3ad9abe53?s=80&d=identicon", + "name": "noyez", + "username": "noyez" + } +} diff --git a/src/test/types.rs b/src/test/types.rs index d4ef316fc580dd74159aff1923a082fa13131cc8..f80a3224b204832c9ba20e0226d40de446658e32 100644 --- a/src/test/types.rs +++ b/src/test/types.rs @@ -11,6 +11,7 @@ use crates::serde::de::DeserializeOwned; use crates::serde_json::from_reader; use types::*; +use webhooks::*; use std::fs::File; @@ -20,6 +21,44 @@ fn read_test_file(name: &str) -> T { from_reader::(fin).unwrap() } +#[test] +fn test_read_pipeline_1() { + let pipeline_hook: PipelineHook = read_test_file("pipeline2"); +} + +#[test] +fn test_read_pipeline_2() { + let pipeline_hook: PipelineHook = read_test_file("pipeline2"); +} + +#[test] +fn test_read_pipeline_3() { + let pipeline_hook: PipelineHook = read_test_file("pipeline3"); +} + +#[test] +fn test_read_pipeline_4() { + let pipeline_hook: PipelineHook = read_test_file("pipeline4"); +} + +#[test] +fn test_read_pipeline_5() { + let pipeline_hook: PipelineHook = read_test_file("pipeline5"); +} + + +#[test] +fn test_read_pipeline_6() { + let pipeline_hook: PipelineHook = read_test_file("pipeline6"); +} +#[test] +fn test_read_pipeline_7() { + let pipeline_hook: PipelineHook = read_test_file("pipeline7"); +} +#[test] +fn test_read_pipeline_8() { + let pipeline_hook: PipelineHook = read_test_file("pipeline8"); +} #[test] fn test_read_award_emoji() { diff --git a/src/types.rs b/src/types.rs index dd3b2121481f02267931f3696c8e19abd8567d3d..7dc9b7551cd70a717274e13459291d762743f59b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1893,6 +1893,8 @@ pub enum StatusState { Pending, /// The check is currently running. Running, + /// The check passed. + Passed, /// The check succeeded. Success, /// The check failed. @@ -1908,6 +1910,7 @@ pub enum StatusState { enum_serialize!(StatusState -> "status state", Created => "created", Pending => "pending", + Passed => "passed", Running => "running", Success => "success", Failed => "failed", @@ -2121,7 +2124,7 @@ pub struct Runner { /// An uploaded artifact from a job. pub struct JobArtifactFile { /// The name of the artifact. - pub filename: String, + pub filename: Option, /// The size (in bytes) of the artifact. pub size: usize, } diff --git a/src/webhooks.rs b/src/webhooks.rs index db55ba56fa3ac776c28cf09d28b733828d971744..e10e3f6d5694f665da9be859de4dae9d24dda896 100644 --- a/src/webhooks.rs +++ b/src/webhooks.rs @@ -20,7 +20,8 @@ use crates::serde_json::{self, Value}; use types::{JobId, IssueId, IssueInternalId, IssueState, MergeRequestId, MergeRequestInternalId, MergeRequestState, MergeStatus, - MilestoneId, NoteId, NoteType, NoteableId, ObjectId, ProjectId, SnippetId, UserId}; + MilestoneId, NoteId, NoteType, NoteableId, ObjectId, ProjectId, SnippetId, UserId, JobArtifactFile, Runner, StatusState, User, PipelineId}; + #[derive(Debug, Clone, Copy)] /// A wrapper struct for dates in web hooks. @@ -60,6 +61,29 @@ impl AsRef> for HookDate { } } +#[derive(Deserialize)] +/// A wrapper struct helper struct for gitlab's mixed date format +/// +/// Gitlab does not use a standard date format for dates in web hooks. This structure supports +/// deserializing from either format into a consistant DateTime +#[serde(untagged)] +enum ChronoDtOrHookDtHelper { ChronoDt(DateTime), HookDt(HookDate) } +fn chrono_dt_or_hook_dt<'de, D>(deserializer: D) -> Result, D::Error> + where D: Deserializer<'de> +{ + match ChronoDtOrHookDtHelper::deserialize(deserializer)? { + ChronoDtOrHookDtHelper::ChronoDt(v) => { Ok(v) } + ChronoDtOrHookDtHelper::HookDt(v) => { Ok(v.0) } + } +} + +fn chrono_dt_or_hook_dt_opt<'de, D>(deserializer: D) -> Result>, D::Error> + where D: Deserializer<'de> +{ + //chrono_dt_or_hook_dt(deserializer).map(|x| Some(x)).or(Ok(None)) + chrono_dt_or_hook_dt(deserializer).map(|x| Some(x)).or(Ok(None)) +} + #[cfg_attr(feature="strict", serde(deny_unknown_fields))] #[derive(Serialize, Deserialize, Debug, Clone)] /// Project information exposed in hooks. @@ -84,10 +108,15 @@ pub struct ProjectHookAttrs { pub path_with_namespace: String, /// The default branch for the project. pub default_branch: Option, - homepage: String, - http_url: String, - ssh_url: String, - url: String, + /// Project id + pub id: u64, + /// ... + pub ci_config_path: Option, + + homepage: Option, + http_url: Option, + ssh_url: Option, + url: Option, } #[cfg_attr(feature="strict", serde(deny_unknown_fields))] @@ -146,6 +175,84 @@ pub struct CommitHookAttrs { pub removed: Option>, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BuildAttrs { + pub artifacts_file : JobArtifactFile, + /// When the hook was created. + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub created_at: DateTime, + /// When the hook was finished + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub finished_at: Option>, + /// When the hook was finished + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub started_at: Option>, + /// The ID of the build. + pub id: JobId, + /// Manual job. + pub manual: bool, + /// Job name + pub name: String, + /// Runner + pub runner: Option, + /// Build stage name + pub stage: String, + /// Status of the build + pub status: StatusState, + /// The user which triggered the hook. + pub user: UserHookAttrs, + /// How build was triggered + pub when: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PipelineCommitAuthorAttrs { + pub email: String, + pub name: String, +} + +//#[cfg_attr(feature="strict", serde(deny_unknown_fields))] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PipelineHookAttrs { + /// + pub before_sha: ObjectId, + /// Date pipeline created + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub created_at: DateTime, + /// Status of Pipeline + pub detailed_status: StatusState, + /// Duration of pipeline (in seconds) + pub duration: Option, + /// Time pipeline finished. + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub finished_at: Option>, + /// Id of pipeline + pub id: PipelineId, + /// References for pipeline, branch or tag. + #[serde(rename="ref")] + pub ref_: String, + pub sha: ObjectId, + /// All stages of pipeline + pub stages: Vec, + /// Status of pipeline + pub status: StatusState, + pub tag: bool, + /// Variables used to during pipeline + pub variables: Vec, +} + +#[cfg_attr(feature="strict", serde(deny_unknown_fields))] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// A Pipeline hook. +pub struct PipelineHook { + pub builds : Vec, + pub commit : CommitHookAttrs, + pub object_attributes: PipelineHookAttrs, + pub object_kind : String, + pub project: ProjectHookAttrs, + pub user: UserHookAttrs, +} + #[cfg_attr(feature="strict", serde(deny_unknown_fields))] #[derive(Serialize, Deserialize, Debug, Clone)] /// A push hook. @@ -222,13 +329,17 @@ pub struct IssueHookAttrs { /// The ID of the project. pub project_id: ProjectId, /// When the issue was created. - pub created_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub created_at: DateTime, /// When the issue was last updated. - pub updated_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub updated_at: DateTime, /// When the issue was deleted. - pub deleted_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub deleted_at: Option>, /// When the issue was closed. - pub closed_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub closed_at: Option>, /// When the issue is due. pub due_date: Option, /// The ID of the user which last updated the issue. @@ -361,13 +472,17 @@ pub struct MergeRequestHookAttrs { /// The title of the merge request. pub title: String, /// When the merge request was created. - pub created_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub created_at: DateTime, /// When the merge request was last updated. - pub updated_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub updated_at: DateTime, /// When the merge request was deleted. - pub deleted_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub deleted_at: Option>, /// When the merge request was locked. - pub locked_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub locked_at: Option>, /// The ID of the user which last updated the merge request. pub updated_by_id: Option, /// The object ID of the commit which merged the merge request. @@ -445,9 +560,11 @@ pub struct SnippetHookAttrs { /// The project the snippet belongs to. pub project_id: Option, /// When the snippet was created. - pub created_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub created_at: DateTime, /// When the snippet was last updated. - pub updated_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub updated_at: DateTime, /// The name of the snippet. pub file_name: String, #[serde(rename="type")] @@ -545,13 +662,16 @@ pub struct NoteHookAttrs { /// The author of the note. pub author_id: UserId, /// When the note was created. - pub created_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub created_at: DateTime, /// When the note was last updated. - pub updated_at: HookDate, + #[serde(deserialize_with = "chrono_dt_or_hook_dt")] + pub updated_at: DateTime, /// The ID of the user who last updated the note. pub updated_by_id: Option, /// When the note was marked as resolved. - pub resolved_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub resolved_at: Option>, /// The ID of the user who marked the note as resolved. pub resolved_by_id: Option, /// The ID of the project. @@ -655,9 +775,11 @@ pub struct BuildCommitHookAttrs { pub status: String, pub duration: u64, /// When the build started. - pub started_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub started_at: Option>, /// When the build completed. - pub finished_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub finished_at: Option>, } #[cfg_attr(feature="strict", serde(deny_unknown_fields))] @@ -697,9 +819,11 @@ pub struct BuildHook { pub build_name: String, pub build_stage: String, /// When the build started. - pub build_started_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub build_started_at: Option>, /// When the build completed. - pub build_finished_at: Option, + #[serde(deserialize_with = "chrono_dt_or_hook_dt_opt")] + pub build_finished_at: Option>, pub build_duration: Option, /// Whether the build is allowed to fail. pub build_allow_failure: bool,