212 lines
5.7 KiB
Rust
212 lines
5.7 KiB
Rust
pub mod blob;
|
|
pub mod branch;
|
|
pub mod cache_helper;
|
|
pub mod commit;
|
|
pub mod tag;
|
|
pub mod tree;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use actix_web::{HttpResponse, web};
|
|
use cache::AppCache;
|
|
use db::database::AppDatabase;
|
|
use juniper::{
|
|
EmptyMutation, EmptySubscription, FieldResult, RootNode, graphql_object,
|
|
};
|
|
use serde_json::json;
|
|
use track::CounterVec;
|
|
|
|
use crate::{
|
|
bare::GitBare,
|
|
graphql::{
|
|
blob::{BlobResult, resolve_blob, resolve_blob_size},
|
|
branch::{
|
|
BranchGql, BranchSummaryGql, resolve_branch,
|
|
resolve_branch_summary, resolve_branches, resolve_head_branch,
|
|
},
|
|
commit::{
|
|
CommitGql, CommitSummaryGql, resolve_commit,
|
|
resolve_commit_history, resolve_commit_summary,
|
|
},
|
|
tag::{
|
|
TagGql, TagSummaryGql, resolve_tag, resolve_tag_summary,
|
|
resolve_tags,
|
|
},
|
|
tree::{TreeEntryGql, TreeInfoGql, resolve_tree, resolve_tree_entries},
|
|
},
|
|
};
|
|
|
|
type Schema = RootNode<
|
|
GraphqlQuery,
|
|
EmptyMutation<GraphqlContext>,
|
|
EmptySubscription<GraphqlContext>,
|
|
>;
|
|
|
|
fn schema() -> Schema {
|
|
Schema::new(
|
|
GraphqlQuery,
|
|
EmptyMutation::<GraphqlContext>::new(),
|
|
EmptySubscription::<GraphqlContext>::new(),
|
|
)
|
|
}
|
|
|
|
pub async fn graphql_handle(
|
|
path: web::Path<(String, String)>,
|
|
state: web::Data<crate::http::HttpAppState>,
|
|
body: web::Json<juniper::http::GraphQLRequest>,
|
|
) -> HttpResponse {
|
|
let (wk, repo_name) = path.into_inner();
|
|
let repo = match state.git_state.repo(wk, repo_name).await {
|
|
Ok(repo) => repo,
|
|
Err(err) => {
|
|
return HttpResponse::InternalServerError().json(json!({
|
|
"message": err.to_string()
|
|
}));
|
|
}
|
|
};
|
|
|
|
let ctx = GraphqlContext {
|
|
repo: GitBare {
|
|
bare_dir: PathBuf::from(&repo.repo.storage_path),
|
|
},
|
|
cache: state.git_state.cache.clone(),
|
|
db: state.git_state.db.clone(),
|
|
};
|
|
|
|
let schema = schema();
|
|
let response = body.execute(&schema, &ctx).await;
|
|
|
|
let status_code = if response.is_ok() { 200 } else { 400 };
|
|
record_graphql_metric(&state, response.is_ok());
|
|
HttpResponse::build(
|
|
actix_web::http::StatusCode::from_u16(status_code).unwrap(),
|
|
)
|
|
.content_type("application/json")
|
|
.json(response)
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct GraphqlContext {
|
|
pub repo: GitBare,
|
|
pub cache: AppCache,
|
|
pub db: AppDatabase,
|
|
}
|
|
|
|
impl juniper::Context for GraphqlContext {}
|
|
|
|
pub struct GraphqlQuery;
|
|
|
|
fn to_field_error(e: anyhow::Error) -> juniper::FieldError {
|
|
juniper::FieldError::new(e.to_string(), juniper::Value::null())
|
|
}
|
|
|
|
#[graphql_object]
|
|
#[graphql(context = GraphqlContext)]
|
|
impl GraphqlQuery {
|
|
fn api_version() -> &'static str {
|
|
env!("CARGO_PKG_VERSION")
|
|
}
|
|
|
|
async fn head_branch(ctx: &GraphqlContext) -> FieldResult<BranchGql> {
|
|
resolve_head_branch(ctx).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn branches(ctx: &GraphqlContext) -> FieldResult<Vec<BranchGql>> {
|
|
resolve_branches(ctx).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn branch(
|
|
ctx: &GraphqlContext,
|
|
name: String,
|
|
) -> FieldResult<BranchGql> {
|
|
resolve_branch(ctx, name).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn branch_summary(
|
|
ctx: &GraphqlContext,
|
|
) -> FieldResult<BranchSummaryGql> {
|
|
resolve_branch_summary(ctx).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn tags(ctx: &GraphqlContext) -> FieldResult<Vec<TagGql>> {
|
|
resolve_tags(ctx).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn tag(ctx: &GraphqlContext, name: String) -> FieldResult<TagGql> {
|
|
resolve_tag(ctx, name).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn tag_summary(ctx: &GraphqlContext) -> FieldResult<TagSummaryGql> {
|
|
resolve_tag_summary(ctx).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn commit(
|
|
ctx: &GraphqlContext,
|
|
oid: String,
|
|
) -> FieldResult<CommitGql> {
|
|
resolve_commit(ctx, oid).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn commit_history(
|
|
ctx: &GraphqlContext,
|
|
limit: Option<i32>,
|
|
skip: Option<i32>,
|
|
sort: Option<String>,
|
|
) -> FieldResult<Vec<CommitGql>> {
|
|
resolve_commit_history(ctx, limit, skip, sort)
|
|
.await
|
|
.map_err(to_field_error)
|
|
}
|
|
|
|
async fn commit_summary(
|
|
ctx: &GraphqlContext,
|
|
) -> FieldResult<CommitSummaryGql> {
|
|
resolve_commit_summary(ctx).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn tree(
|
|
ctx: &GraphqlContext,
|
|
oid: String,
|
|
) -> FieldResult<TreeInfoGql> {
|
|
resolve_tree(ctx, oid).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn tree_entries(
|
|
ctx: &GraphqlContext,
|
|
tree_oid: String,
|
|
) -> FieldResult<Vec<TreeEntryGql>> {
|
|
resolve_tree_entries(ctx, tree_oid)
|
|
.await
|
|
.map_err(to_field_error)
|
|
}
|
|
|
|
async fn blob_size(ctx: &GraphqlContext, oid: String) -> FieldResult<i32> {
|
|
resolve_blob_size(ctx, oid).await.map_err(to_field_error)
|
|
}
|
|
|
|
async fn blob(
|
|
ctx: &GraphqlContext,
|
|
oid: String,
|
|
) -> FieldResult<BlobResult> {
|
|
resolve_blob(ctx, oid).await.map_err(to_field_error)
|
|
}
|
|
}
|
|
|
|
fn record_graphql_metric(state: &crate::http::HttpAppState, ok: bool) {
|
|
if let Some(reg) = &state.metrics {
|
|
graphql_counter(reg)
|
|
.with_label_values(&[if ok { "success" } else { "error" }])
|
|
.inc();
|
|
}
|
|
}
|
|
|
|
fn graphql_counter(registry: &track::MetricsRegistry) -> CounterVec {
|
|
registry
|
|
.register_counter_vec(
|
|
"git_graphql_queries_total",
|
|
"Total Git GraphQL queries",
|
|
&["outcome"],
|
|
)
|
|
.expect("failed to register git_graphql_queries_total")
|
|
}
|