Implement endpoint for gitlab
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Valentin Brandl 2019-07-26 17:06:21 +02:00
parent ff36e59b28
commit 865cf36bcf
No known key found for this signature in database
GPG Key ID: 30D341DD34118D7D
2 changed files with 153 additions and 31 deletions

View File

@ -11,10 +11,10 @@ mod statics;
use crate::{ use crate::{
data::FilePath, data::FilePath,
error::Result, error::Result,
service::{ApiResponse, Bitbucket, Github, Service}, service::{Bitbucket, GitLab, Github, Service},
}; };
use actix_web::{ use actix_web::{
http::header::{self, CacheControl, CacheDirective, Expires, LOCATION}, http::header::{self, CacheControl, CacheDirective, Expires},
middleware, web, App, Error, HttpResponse, HttpServer, middleware, web, App, Error, HttpResponse, HttpServer,
}; };
use awc::{http::StatusCode, Client}; use awc::{http::StatusCode, Client};
@ -64,30 +64,7 @@ fn redirect<T: Service>(
.header(header::USER_AGENT, statics::USER_AGENT.as_str()) .header(header::USER_AGENT, statics::USER_AGENT.as_str())
.send() .send()
.from_err() .from_err()
.and_then(move |mut response| match response.status() { .and_then(move |response| T::request_head(response, data, client)),
StatusCode::OK => Box::new(
response
.json::<T::Response>()
.map(move |resp| {
HttpResponse::SeeOther()
.header(
LOCATION,
T::redirect_url(
&data.user,
&data.repo,
resp.commit_ref(),
&data.file,
)
.as_str(),
)
.finish()
})
.from_err(),
)
as Box<dyn Future<Item = HttpResponse, Error = Error>>,
code => Box::new(futures::future::ok(HttpResponse::build(code).finish()))
as Box<dyn Future<Item = HttpResponse, Error = Error>>,
}),
) )
} }
@ -104,7 +81,7 @@ fn handle_request<T: Service>(
fn main() -> Result<()> { fn main() -> Result<()> {
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=trace"); std::env::set_var("RUST_LOG", "actix_server=info,actix_web=trace");
env_logger::init(); pretty_env_logger::init();
openssl_probe::init_ssl_cert_env_vars(); openssl_probe::init_ssl_cert_env_vars();
Ok(HttpServer::new(move || { Ok(HttpServer::new(move || {
@ -119,9 +96,11 @@ fn main() -> Result<()> {
"/bitbucket/{user}/{repo}/{commit}/{file:.*}", "/bitbucket/{user}/{repo}/{commit}/{file:.*}",
web::get().to_async(handle_request::<Bitbucket>), web::get().to_async(handle_request::<Bitbucket>),
) )
// .default_service(web::resource("").route(web::get().to_async(p404))) .route(
"/gitlab/{user}/{repo}/{commit}/{file:.*}",
web::get().to_async(handle_request::<GitLab>),
)
}) })
// .workers(OPT.workers)
.bind("0.0.0.0:8080")? .bind("0.0.0.0:8080")?
.run()?) .run()?)
} }

View File

@ -1,4 +1,11 @@
use crate::data::FilePath; use crate::data::FilePath;
use actix_web::{
http::{header::LOCATION, StatusCode},
web, Error, HttpResponse,
};
use awc::{error::PayloadError, Client, ClientResponse};
use bytes::Bytes;
use futures::{Future, Stream};
pub(crate) trait ApiResponse { pub(crate) trait ApiResponse {
fn commit_ref(&self) -> &str; fn commit_ref(&self) -> &str;
@ -21,8 +28,8 @@ pub(crate) struct BitbucketApiResponse {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub(crate) struct BitbucketEntry { struct BitbucketEntry {
pub(crate) hash: String, hash: String,
} }
impl ApiResponse for BitbucketApiResponse { impl ApiResponse for BitbucketApiResponse {
@ -31,11 +38,64 @@ impl ApiResponse for BitbucketApiResponse {
} }
} }
#[derive(Deserialize)]
struct GitLabProject {
id: u64,
}
#[derive(Deserialize)]
pub(crate) struct GitLabApiResponse {
commit: GitLabCommit,
}
#[derive(Deserialize)]
struct GitLabCommit {
id: String,
}
impl ApiResponse for GitLabApiResponse {
fn commit_ref(&self) -> &str {
&self.commit.id
}
}
pub(crate) trait Service { pub(crate) trait Service {
type Response: for<'de> serde::Deserialize<'de> + ApiResponse + 'static; type Response: for<'de> serde::Deserialize<'de> + ApiResponse + 'static;
fn raw_url(user: &str, repo: &str, commit: &str, file: &str) -> String; fn raw_url(user: &str, repo: &str, commit: &str, file: &str) -> String;
fn api_url(path: &FilePath) -> String; fn api_url(path: &FilePath) -> String;
fn redirect_url(user: &str, repo: &str, commit: &str, file: &str) -> String; fn redirect_url(user: &str, repo: &str, commit: &str, file: &str) -> String;
fn request_head<S>(
mut response: ClientResponse<S>,
data: web::Path<FilePath>,
_client: web::Data<Client>,
) -> Box<dyn Future<Item = HttpResponse, Error = Error>>
where
S: 'static + Stream<Item = Bytes, Error = PayloadError>,
{
Box::new(match response.status() {
StatusCode::OK => Box::new(
response
.json::<Self::Response>()
.map(move |resp| {
HttpResponse::SeeOther()
.header(
LOCATION,
Self::redirect_url(
&data.user,
&data.repo,
resp.commit_ref(),
&data.file,
)
.as_str(),
)
.finish()
})
.from_err(),
) as Box<dyn Future<Item = HttpResponse, Error = Error>>,
code => Box::new(futures::future::ok(HttpResponse::build(code).finish()))
as Box<dyn Future<Item = HttpResponse, Error = Error>>,
})
}
} }
pub(crate) struct Github; pub(crate) struct Github;
@ -85,3 +145,86 @@ impl Service for Bitbucket {
format!("/bitbucket/{}/{}/{}/{}", user, repo, commit, file) format!("/bitbucket/{}/{}/{}/{}", user, repo, commit, file)
} }
} }
pub(crate) struct GitLab;
impl Service for GitLab {
type Response = GitLabApiResponse;
fn raw_url(user: &str, repo: &str, commit: &str, file: &str) -> String {
format!(
"https://gitlab.com/{}/{}/raw/{}/{}",
user, repo, commit, file
)
}
fn api_url(path: &FilePath) -> String {
let repo_pattern = format!("{}/{}", path.user, path.repo).replace("/", "%2F");
format!("https://gitlab.com/api/v4/projects/{}", repo_pattern)
// format!(
// "https://gitlab.com/api/v4/projects/{}/repository/branches/{}",
// path.repo, path.commit
// )
}
fn redirect_url(user: &str, repo: &str, commit: &str, file: &str) -> String {
format!("/gitlab/{}/{}/{}/{}", user, repo, commit, file)
}
fn request_head<S>(
mut response: ClientResponse<S>,
data: web::Path<FilePath>,
client: web::Data<Client>,
) -> Box<dyn Future<Item = HttpResponse, Error = Error>>
where
S: 'static + Stream<Item = Bytes, Error = PayloadError>,
{
// "https://gitlab.com/api/v4/projects/{}/repository/branches/{}",
Box::new(match response.status() {
StatusCode::OK => Box::new(
response
.json::<GitLabProject>()
.map(move |resp| resp.id)
.from_err()
.and_then(move |repo_id| {
client
.get(format!(
"https://gitlab.com/api/v4/projects/{}/repository/branches/{}",
repo_id, data.commit
))
.send()
.from_err()
.and_then(|mut respo| match respo.status() {
StatusCode::OK => Box::new(
respo
.json::<Self::Response>()
.map(move |resp| {
HttpResponse::SeeOther()
.header(
LOCATION,
Self::redirect_url(
&data.user,
&data.repo,
resp.commit_ref(),
&data.file,
)
.as_str(),
)
.finish()
})
.from_err(),
)
as Box<dyn Future<Item = HttpResponse, Error = Error>>,
code => Box::new(futures::future::ok(
HttpResponse::build(code).finish(),
))
as Box<dyn Future<Item = HttpResponse, Error = Error>>,
})
.from_err()
}),
) as Box<dyn Future<Item = HttpResponse, Error = Error>>,
code => Box::new(futures::future::ok(HttpResponse::build(code).finish()))
as Box<dyn Future<Item = HttpResponse, Error = Error>>,
})
}
}