GitLab CI + v3 Endpoint #1
This commit is contained in:
parent
e19187d6c7
commit
790a7dd8ee
4 changed files with 331 additions and 1 deletions
68
.gitlab-ci.yml
Normal file
68
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
image: 3.10.8-slim-buster
|
||||||
|
|
||||||
|
variables:
|
||||||
|
PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/linux/$CI_COMMIT_TAG/"
|
||||||
|
|
||||||
|
.deploy:
|
||||||
|
rules:
|
||||||
|
# Regex magic copied from Neshura/page-test, only deploys on x.y.z or higher (x.y) Tags
|
||||||
|
- if: $CI_COMMIT_TAG =~ /^((([\d])+\.){1,2}[\d]+)\s*$/ && $CI_COMMIT_TAG
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- upload
|
||||||
|
- release
|
||||||
|
|
||||||
|
## Docker steps
|
||||||
|
|
||||||
|
build:
|
||||||
|
image: rust:latest
|
||||||
|
stage: build
|
||||||
|
|
||||||
|
variables:
|
||||||
|
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_BRANCH
|
||||||
|
CACHING:
|
||||||
|
script:
|
||||||
|
- echo "Compiling the code..."
|
||||||
|
- cargo build -r
|
||||||
|
- echo "Compile complete."
|
||||||
|
after_script:
|
||||||
|
- echo JOB_ID=$CI_JOB_ID >> job.env
|
||||||
|
- mkdir ./artifacts
|
||||||
|
- cp /builds/Neshura/chellaris-rust-api/target/release/chellaris-rust-api ./artifacts/
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- ./artifacts/
|
||||||
|
reports:
|
||||||
|
dotenv: job.env
|
||||||
|
rules:
|
||||||
|
- !reference [.deploy, rules]
|
||||||
|
|
||||||
|
upload:
|
||||||
|
needs:
|
||||||
|
- job: build
|
||||||
|
artifacts: true
|
||||||
|
image: curlimages/curl:latest
|
||||||
|
stage: upload
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file artifacts/chellaris-rust-api "${PACKAGE_REGISTRY_URL}/chellaris-rust-api"
|
||||||
|
rules:
|
||||||
|
- !reference [.deploy, rules]
|
||||||
|
|
||||||
|
|
||||||
|
Tag Release:
|
||||||
|
stage: release
|
||||||
|
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||||
|
rules:
|
||||||
|
- !reference [.deploy, rules]
|
||||||
|
script:
|
||||||
|
- apk add curl
|
||||||
|
- echo "running Release Job, attaching Artifact from Job $JOB_ID"
|
||||||
|
release:
|
||||||
|
tag_name: '$CI_COMMIT_TAG'
|
||||||
|
description: '$CI_COMMIT_TAG'
|
||||||
|
assets:
|
||||||
|
links:
|
||||||
|
- name: "chellaris-rust-api"
|
||||||
|
url: "${PACKAGE_REGISTRY_URL}/chellaris-rust-api"
|
28
src/main.rs
28
src/main.rs
|
@ -12,6 +12,7 @@ use crate::db::connect_postgres;
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
mod v2;
|
mod v2;
|
||||||
|
mod v3;
|
||||||
|
|
||||||
macro_rules! api_base {
|
macro_rules! api_base {
|
||||||
() => {
|
() => {
|
||||||
|
@ -75,6 +76,15 @@ async fn postgres_watchdog(pool: PgPool, is_alive: Arc<AtomicBool>, shutdown: Ar
|
||||||
};
|
};
|
||||||
|
|
||||||
let passed = (Local::now() - start).to_std().expect(&format!("Unable to get Time Difference for '{}' and '{}'", start, Local::now()));
|
let passed = (Local::now() - start).to_std().expect(&format!("Unable to get Time Difference for '{}' and '{}'", start, Local::now()));
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_secs(5) - passed);
|
||||||
|
if shutdown.load(Ordering::Relaxed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thread::sleep(Duration::from_secs(10) - passed);
|
||||||
|
if shutdown.load(Ordering::Relaxed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
thread::sleep(Duration::from_secs(15) - passed);
|
thread::sleep(Duration::from_secs(15) - passed);
|
||||||
}
|
}
|
||||||
is_alive.store(false, Ordering::Relaxed);
|
is_alive.store(false, Ordering::Relaxed);
|
||||||
|
@ -121,7 +131,21 @@ async fn main() -> Result<()> {
|
||||||
struct ApiDocV2;
|
struct ApiDocV2;
|
||||||
|
|
||||||
#[derive(OpenApi)]
|
#[derive(OpenApi)]
|
||||||
#[openapi(paths(), components(schemas()))]
|
#[openapi(
|
||||||
|
paths(
|
||||||
|
v3::full_view_data,
|
||||||
|
),
|
||||||
|
components(schemas(
|
||||||
|
v3::schemas::FullViewData,
|
||||||
|
v3::schemas::Ethic,
|
||||||
|
v3::schemas::EmpireEthic,
|
||||||
|
v3::schemas::ChellarisGame,
|
||||||
|
v3::schemas::Species,
|
||||||
|
v3::schemas::ChellarisGameGroup,
|
||||||
|
v3::schemas::Portrait,
|
||||||
|
v3::schemas::ChellarisEmpire
|
||||||
|
))
|
||||||
|
)]
|
||||||
struct ApiDocV3;
|
struct ApiDocV3;
|
||||||
|
|
||||||
let openapi_urls = vec![
|
let openapi_urls = vec![
|
||||||
|
@ -156,6 +180,8 @@ async fn main() -> Result<()> {
|
||||||
.service(v2::games)
|
.service(v2::games)
|
||||||
.service(v2::portrait_groups)
|
.service(v2::portrait_groups)
|
||||||
.service(v2::portraits)
|
.service(v2::portraits)
|
||||||
|
// API v3 Endpoints
|
||||||
|
.service(v3::full_view_data)
|
||||||
// Swagger UI
|
// Swagger UI
|
||||||
.service(
|
.service(
|
||||||
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
|
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
|
||||||
|
|
165
src/v3/mod.rs
Normal file
165
src/v3/mod.rs
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
use std::{vec, collections::HashMap};
|
||||||
|
|
||||||
|
use actix_web::{web::{self, Json}, Responder, get};
|
||||||
|
|
||||||
|
use crate::{AppState, db};
|
||||||
|
|
||||||
|
pub(crate) mod schemas;
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
params(
|
||||||
|
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "OK", body = FullViewData),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("api_key" = [])
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
#[get("/full_view_data")]
|
||||||
|
pub(crate) async fn full_view_data(
|
||||||
|
data: web::Data<AppState>
|
||||||
|
) -> impl Responder {
|
||||||
|
let start = chrono::Local::now(); // DEBUG
|
||||||
|
|
||||||
|
// SQL Queries
|
||||||
|
// TODO: Optimize by utilizing some SQL magic, for now this has to do
|
||||||
|
|
||||||
|
let db_games: Vec<db::schemas::Game> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.games ORDER BY id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let db_groups: Vec<db::schemas::GameGroup> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.game_groups ORDER BY id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let db_empires: Vec<db::schemas::Empire> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.empires ORDER BY id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let db_ethics: Vec<db::schemas::Ethic> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.ethics ORDER BY id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let db_empire_ethics: Vec<db::schemas::EmpireEthic> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.empire_ethics ORDER BY empires_id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let db_portrait_groups: Vec<db::schemas::PortraitGroup> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.portrait_groups ORDER BY id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let db_portraits: Vec<db::schemas::Portrait> = sqlx::query_as(
|
||||||
|
"SELECT * FROM public.portraits ORDER BY id",
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
|
let mut parsed_data: schemas::FullViewData = schemas::FullViewData {
|
||||||
|
games: HashMap::new(),
|
||||||
|
ethics: HashMap::new(),
|
||||||
|
species: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Data processing
|
||||||
|
// Species Vector
|
||||||
|
db_portrait_groups.iter().for_each(|species| {
|
||||||
|
let new_data = schemas::Species {
|
||||||
|
id: species.id,
|
||||||
|
displayName: species.name.clone(),
|
||||||
|
portraits: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.species.entry(species.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
db_portraits.iter().for_each(|portrait| {
|
||||||
|
let new_data = schemas::Portrait {
|
||||||
|
id: portrait.id,
|
||||||
|
hires: portrait.hires.clone(),
|
||||||
|
lores: portrait.lores.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.species.get_mut(&portrait.group_id).unwrap().portraits.entry(portrait.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Games Vector and Children
|
||||||
|
db_games.iter().for_each(|game| {
|
||||||
|
let new_data = schemas::ChellarisGame {
|
||||||
|
id: game.id,
|
||||||
|
name: game.name.clone(),
|
||||||
|
empires: HashMap::new(),
|
||||||
|
groups: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.games.entry(game.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
db_groups.iter().for_each(|group| {
|
||||||
|
let new_data = schemas::ChellarisGameGroup {
|
||||||
|
id: group.id,
|
||||||
|
name: group.name.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.games.get_mut(&group.game_id).unwrap().groups.entry(group.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
db_empires.iter().for_each(|empire| {
|
||||||
|
let new_data = schemas::ChellarisEmpire {
|
||||||
|
id: empire.id,
|
||||||
|
gestalt: empire.gestalt.unwrap_or(false),
|
||||||
|
machine: false, // TODO overwrite this later on with correct data
|
||||||
|
group: empire.group_id,
|
||||||
|
empire_portrait: empire.empire_portrait_id,
|
||||||
|
empire_portrait_group: empire.empire_portrait_group_id,
|
||||||
|
discord_user: empire.discord_user.clone(),
|
||||||
|
ethics: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.games.get_mut(&empire.group_game_id).unwrap().empires.entry(empire.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ethics Vector
|
||||||
|
db_ethics.iter().for_each(|ethic| {
|
||||||
|
let new_data = schemas::Ethic {
|
||||||
|
id: ethic.id,
|
||||||
|
displayName: ethic.name.clone(),
|
||||||
|
machine: ethic.machine_ethic,
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.ethics.entry(ethic.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("{:#?}", parsed_data.ethics); // DEBUG
|
||||||
|
|
||||||
|
db_empire_ethics.iter().for_each(|empire_ethic| {
|
||||||
|
let game_id = empire_ethic.empires_group_game_id;
|
||||||
|
let id = empire_ethic.empires_id;
|
||||||
|
|
||||||
|
let new_data = schemas::EmpireEthic {
|
||||||
|
id: empire_ethic.ethics_id,
|
||||||
|
displayName: parsed_data.ethics[&empire_ethic.ethics_id].displayName.clone(),
|
||||||
|
machine: parsed_data.ethics[&empire_ethic.ethics_id].machine,
|
||||||
|
fanatic: empire_ethic.ethics_fanatic,
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data.games.get_mut(&game_id).unwrap().empires.get_mut(&id).unwrap().ethics.entry(empire_ethic.ethics_id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("{:?} ms", (chrono::Local::now() - start).to_std().unwrap()); // DEBUG
|
||||||
|
|
||||||
|
Json(parsed_data)
|
||||||
|
}
|
71
src/v3/schemas.rs
Normal file
71
src/v3/schemas.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use utoipa::{ToSchema, IntoParams};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
||||||
|
pub struct AuthParams {
|
||||||
|
pub token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct FullViewData {
|
||||||
|
pub games: HashMap<i32, ChellarisGame>,
|
||||||
|
pub ethics: HashMap<i32, Ethic>,
|
||||||
|
pub species: HashMap<i32, Species>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct ChellarisGame {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub groups: HashMap<i32, ChellarisGameGroup>,
|
||||||
|
pub empires: HashMap<i32, ChellarisEmpire>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct ChellarisGameGroup {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct ChellarisEmpire {
|
||||||
|
pub id: i32,
|
||||||
|
pub gestalt: bool,
|
||||||
|
pub machine: bool,
|
||||||
|
pub group: i32,
|
||||||
|
pub empire_portrait: i32,
|
||||||
|
pub empire_portrait_group: i32,
|
||||||
|
pub discord_user: Option<String>,
|
||||||
|
pub ethics: HashMap<i32, EmpireEthic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct Ethic {
|
||||||
|
pub id: i32,
|
||||||
|
pub displayName: String,
|
||||||
|
pub machine: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct EmpireEthic {
|
||||||
|
pub id: i32,
|
||||||
|
pub displayName: String,
|
||||||
|
pub machine: bool,
|
||||||
|
pub fanatic: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct Species {
|
||||||
|
pub id: i32,
|
||||||
|
pub displayName: String,
|
||||||
|
pub portraits: HashMap<i32, Portrait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct Portrait {
|
||||||
|
pub id: i32,
|
||||||
|
pub hires: String,
|
||||||
|
pub lores: Option<String>,
|
||||||
|
}
|
Reference in a new issue