pipedash-plugin-gitlab 0.2.0

GitLab CI/CD plugin for Pipedash
Documentation
use std::collections::HashMap;

use chrono::Utc;
use pipedash_plugin_api::{
    AvailablePipeline,
    Pipeline,
    PipelineRun,
    PipelineStatus,
};

use crate::types;

pub(crate) fn map_status(gitlab_status: &str) -> PipelineStatus {
    match gitlab_status {
        "success" => PipelineStatus::Success,
        "failed" => PipelineStatus::Failed,
        "running" | "pending" => PipelineStatus::Running,
        "canceled" => PipelineStatus::Cancelled,
        "skipped" => PipelineStatus::Skipped,
        _ => PipelineStatus::Pending,
    }
}

pub(crate) fn map_pipeline(
    project: &types::Project, pipeline: Option<&types::Pipeline>, provider_id: i64,
) -> Pipeline {
    let status = pipeline
        .map(|p| map_status(&p.status))
        .unwrap_or(PipelineStatus::Pending);
    let last_run = pipeline.and_then(|p| p.updated_at.into());
    let branch = pipeline.map(|p| p.ref_name.clone());

    let repository_normalized = project.name_with_namespace.replace(" ", "");

    let parts: Vec<&str> = project.name_with_namespace.split('/').collect();
    let namespace = if parts.len() >= 2 {
        parts[..parts.len() - 1].join("/")
    } else {
        String::new()
    };

    let mut metadata = HashMap::new();
    metadata.insert("project_id".to_string(), serde_json::json!(project.id));
    if !namespace.is_empty() {
        metadata.insert("namespace".to_string(), serde_json::json!(namespace));
    }

    Pipeline {
        id: format!("gitlab__{}__{}", provider_id, project.id),
        provider_id,
        provider_type: "gitlab".to_string(),
        name: project.name.clone(),
        status,
        last_run,
        last_updated: Utc::now(),
        repository: repository_normalized,
        branch,
        workflow_file: None,
        metadata,
    }
}

pub(crate) fn map_pipeline_run(
    pipeline: &types::Pipeline, project_id: i64, provider_id: i64, namespace: Option<&str>,
) -> PipelineRun {
    let duration = pipeline.duration.or_else(|| {
        pipeline.started_at.and_then(|started| {
            pipeline
                .finished_at
                .map(|finished| (finished - started).num_seconds())
        })
    });

    let mut metadata = HashMap::new();
    if let Some(ref source) = pipeline.source {
        metadata.insert("source".to_string(), serde_json::json!(source));
    }
    if let Some(ns) = namespace {
        metadata.insert("namespace".to_string(), serde_json::json!(ns));
    }

    PipelineRun {
        id: format!("gitlab__{}__{}_{}", provider_id, project_id, pipeline.id),
        pipeline_id: format!("gitlab__{}__{}", provider_id, project_id),
        run_number: pipeline.id,
        status: map_status(&pipeline.status),
        started_at: pipeline.started_at.unwrap_or(pipeline.created_at),
        concluded_at: pipeline.finished_at,
        duration_seconds: duration,
        logs_url: pipeline.web_url.clone(),
        commit_sha: Some(pipeline.sha.clone()),
        commit_message: None,
        branch: Some(pipeline.ref_name.clone()),
        actor: pipeline.user.as_ref().map(|u| u.username.clone()),
        inputs: None,
        metadata,
    }
}

pub(crate) fn map_available_pipeline(project: &types::Project) -> AvailablePipeline {
    let parts: Vec<&str> = project.name_with_namespace.split('/').collect();
    let (organization, repository) = if parts.len() >= 2 {
        (Some(parts[0].to_string()), Some(parts[1..].join("/")))
    } else {
        (None, Some(project.name.clone()))
    };

    let id_without_spaces = project.name_with_namespace.replace(" ", "");

    AvailablePipeline {
        id: id_without_spaces,
        name: project.name.clone(),
        description: project.description.clone(),
        organization,
        repository,
    }
}