Added Location and location ownership/rights to gamenight.

This commit is contained in:
2025-12-24 14:48:54 +01:00
parent 8a48119c80
commit ff88029a4b
57 changed files with 3034 additions and 995 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
**/*.rs.bk **/*.rs.bk
Cargo.lock Cargo.lock
.vscode .vscode
**/.idea

860
backend-actix/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,8 @@
use std::{fs::{exists, read_dir, remove_dir_all, File}, io::Write, process::Command}; use std::{
fs::{exists, read_dir, remove_dir_all, File},
io::Write,
process::Command,
};
fn main() { fn main() {
println!("cargo::rerun-if-changed=gamenight-api.yaml"); println!("cargo::rerun-if-changed=gamenight-api.yaml");
@@ -8,11 +11,18 @@ fn main() {
remove_dir_all("src/models").unwrap(); remove_dir_all("src/models").unwrap();
} }
let _ = let _ = Command::new("openapi-generator")
Command::new("openapi-generator") .args([
.args(["generate", "-i", "gamenight-api.yaml", "-g", "rust", "--global-property", "models"]) "generate",
.output() "-i",
.expect("Failed to generate models sources for the gamenight API"); "gamenight-api.yaml",
"-g",
"rust",
"--global-property",
"models",
])
.output()
.expect("Failed to generate models sources for the gamenight API");
let mut file = File::create("src/models/mod.rs").unwrap(); let mut file = File::create("src/models/mod.rs").unwrap();
let paths = read_dir("./src/models").unwrap(); let paths = read_dir("./src/models").unwrap();
@@ -21,7 +31,7 @@ fn main() {
let path = path.path(); let path = path.path();
let stem = path.file_stem().unwrap(); let stem = path.file_stem().unwrap();
if stem == "mod" { if stem == "mod" {
continue continue;
} }
let line = format!("pub mod {};\n", stem.to_str().unwrap()); let line = format!("pub mod {};\n", stem.to_str().unwrap());

View File

@@ -38,6 +38,17 @@ paths:
parameters: [] parameters: []
security: security:
- JWT-Auth: [] - JWT-Auth: []
/users:
get:
responses:
'200':
$ref: '#/components/responses/UsersResponse'
'400':
$ref: '#/components/responses/FailureResponse'
'401':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
/user: /user:
post: post:
summary: '' summary: ''
@@ -242,6 +253,69 @@ paths:
$ref: '#/components/requestBodies/OwnedGamesRequest' $ref: '#/components/requestBodies/OwnedGamesRequest'
security: security:
- JWT-Auth: [] - JWT-Auth: []
/location:
get:
responses:
'200':
$ref: '#/components/responses/LocationResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/GetLocationRequest'
security:
- JWT-Auth: []
post:
responses:
'200':
description: 'Ok'
$ref: '#/components/responses/LocationIdResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AddLocationRequest'
security:
- JWT-Auth: []
/locations:
get:
responses:
'200':
$ref: '#/components/responses/LocationsResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
/location_authorize:
post:
responses:
'200':
description: 'Ok'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AuthorizeLocationRequest'
security:
- JWT-Auth: []
/authorized_location_user_ids:
get:
responses:
'200':
$ref: "#/components/responses/UserIdsResponse"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AuthorizedLocationUserIdsRequest'
security:
- JWT-Auth: []
@@ -326,6 +400,14 @@ components:
type: string type: string
required: required:
- user_id - user_id
LocationId:
title: LocationId
type: object
properties:
location_id:
type: string
required:
- location_id
AddGamenightRequestBody: AddGamenightRequestBody:
title: AddGamenightRequestBody title: AddGamenightRequestBody
type: object type: object
@@ -401,7 +483,49 @@ components:
type: array type: array
items: items:
type: string type: string
UserIdsResponse:
type: array
items:
$ref: "#/components/schemas/UserId"
AddLocationRequestBody:
type: object
properties:
name:
type: string
address:
type: string
note:
type: string
required:
- name
Location:
type: object
properties:
id:
type: string
name:
type: string
address:
type: string
note:
type: string
required:
- id
- name
AuthorizeLocationRequestBody:
type: object
properties:
location_id:
type: string
user_id:
type: string
op:
type: string
enum: [grant, revoke]
required:
- location_id
- user_id
- op
requestBodies: requestBodies:
LoginRequest: LoginRequest:
content: content:
@@ -473,6 +597,27 @@ components:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/UserId' $ref: '#/components/schemas/UserId'
GetLocationRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/LocationId'
AddLocationRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/AddLocationRequestBody'
AuthorizeLocationRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizeLocationRequestBody'
AuthorizedLocationUserIdsRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/LocationId'
responses: responses:
TokenResponse: TokenResponse:
description: Example response description: Example response
@@ -500,6 +645,14 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/Gamenight' $ref: '#/components/schemas/Gamenight'
UsersResponse:
description: List of all Users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
GamenightResponse: GamenightResponse:
description: A gamenight being hosted description: A gamenight being hosted
content: content:
@@ -532,6 +685,34 @@ components:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/GameIdsResponse' $ref: '#/components/schemas/GameIdsResponse'
UserIdsResponse:
description: A list of user ids.
content:
application/json:
schema:
$ref: '#/components/schemas/UserIdsResponse'
LocationResponse:
description: A location
content:
application/json:
schema:
$ref: '#/components/schemas/Location'
LocationIdResponse:
description: A location Id
content:
application/json:
schema:
$ref: '#/components/schemas/LocationId'
LocationsResponse:
description: A list of all LocationsResponse
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Location'
securitySchemes: securitySchemes:
JWT-Auth: JWT-Auth:
type: http type: http

View File

@@ -3,14 +3,14 @@ pub mod models;
pub mod request; pub mod request;
use actix_cors::Cors; use actix_cors::Cors;
use actix_web::middleware::Logger;
use actix_web::HttpServer;
use actix_web::App;
use actix_web::http; use actix_web::http;
use actix_web::middleware::Logger;
use actix_web::web; use actix_web::web;
use request::{*, login, register, gamenights}; use actix_web::App;
use tracing_actix_web::TracingLogger; use actix_web::HttpServer;
use gamenight_database::*; use gamenight_database::*;
use request::{gamenights, login, register, *};
use tracing_actix_web::TracingLogger;
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
@@ -26,11 +26,11 @@ async fn main() -> std::io::Result<()> {
HttpServer::new(move || { HttpServer::new(move || {
let cors = Cors::default() let cors = Cors::default()
.allowed_origin("0.0.0.0") .allowed_origin("0.0.0.0")
.allowed_origin_fn(|_origin, _req_head| { true }) .allowed_origin_fn(|_origin, _req_head| true)
.allowed_methods(vec!["GET", "POST"]) .allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE) .allowed_header(http::header::CONTENT_TYPE)
.max_age(3600); .max_age(3600);
App::new() App::new()
.wrap(cors) .wrap(cors)
@@ -44,6 +44,7 @@ async fn main() -> std::io::Result<()> {
.service(gamenight_post) .service(gamenight_post)
.service(gamenight_get) .service(gamenight_get)
.service(get_user) .service(get_user)
.service(get_users)
.service(get_user_unauthenticated) .service(get_user_unauthenticated)
.service(post_join_gamenight) .service(post_join_gamenight)
.service(post_leave_gamenight) .service(post_leave_gamenight)
@@ -55,6 +56,10 @@ async fn main() -> std::io::Result<()> {
.service(post_own_game) .service(post_own_game)
.service(post_disown_game) .service(post_disown_game)
.service(get_owned_games) .service(get_owned_games)
.service(get_locations)
.service(post_location)
.service(post_location_authorize)
.service(get_authorized_location_user_ids)
}) })
.bind(("::1", 8080))? .bind(("::1", 8080))?
.run() .run()

View File

@@ -1,19 +1,22 @@
use std::future::{Ready, ready}; use std::future::{ready, Ready};
use actix_web::{FromRequest, http, HttpRequest, dev::Payload, web::Data}; use actix_web::{dev::Payload, http, web::Data, FromRequest, HttpRequest};
use chrono::Utc; use chrono::Utc;
use jsonwebtoken::{encode, Header, EncodingKey, decode, DecodingKey, Validation}; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use gamenight_database::{user::{get_user, User}, DbPool}; use gamenight_database::{
user::{get_user, User},
DbPool,
};
use super::error::ApiError; use super::error::ApiError;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Claims { pub struct Claims {
exp: i64, exp: i64,
uid: Uuid uid: Uuid,
} }
pub struct AuthUser(pub User); pub struct AuthUser(pub User);
@@ -25,24 +28,31 @@ pub struct AuthUser(pub User);
// pub role: Role, // pub role: Role,
// } // }
impl From<User> for AuthUser { impl From<User> for AuthUser {
fn from(value: User) -> Self { fn from(value: User) -> Self {
Self(value) Self(value)
} }
} }
fn get_claims(req: &HttpRequest) -> Result<Claims, ApiError> { fn get_claims(req: &HttpRequest) -> Result<Claims, ApiError> {
let token = req.headers() let token = req
.headers()
.get(http::header::AUTHORIZATION) .get(http::header::AUTHORIZATION)
.map(|h| h.to_str().unwrap().split_at(7).1.to_string()); .map(|h| h.to_str().unwrap().split_at(7).1.to_string());
let token = token.ok_or(ApiError{ let token = token.ok_or(ApiError {
status: 400, status: 400,
message: "JWT-token was not specified in the Authorization header as Bearer: token".to_string() message: "JWT-token was not specified in the Authorization header as Bearer: token"
.to_string(),
})?; })?;
let secret = "secret"; let secret = "secret";
Ok(decode::<Claims>(token.as_str(), &DecodingKey::from_secret(secret.as_bytes()), &Validation::default())?.claims) Ok(decode::<Claims>(
token.as_str(),
&DecodingKey::from_secret(secret.as_bytes()),
&Validation::default(),
)?
.claims)
} }
pub fn get_token(user: &User) -> Result<String, ApiError> { pub fn get_token(user: &User) -> Result<String, ApiError> {
@@ -55,7 +65,8 @@ pub fn get_token(user: &User) -> Result<String, ApiError> {
Ok(encode( Ok(encode(
&Header::default(), &Header::default(),
&claims, &claims,
&EncodingKey::from_secret(secret.as_bytes()))?) &EncodingKey::from_secret(secret.as_bytes()),
)?)
} }
impl FromRequest for AuthUser { impl FromRequest for AuthUser {
@@ -63,15 +74,15 @@ impl FromRequest for AuthUser {
type Future = Ready<Result<Self, Self::Error>>; type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
ready( ready((|| -> Result<AuthUser, ApiError> {
(|| -> Result<AuthUser, ApiError>{ let pool = req
let pool = req.app_data::<Data<DbPool>>().expect("No database configured"); .app_data::<Data<DbPool>>()
let mut conn = pool.get().expect("couldn't get db connection from pool"); .expect("No database configured");
let uid = get_claims(req)?.uid; let mut conn = pool.get().expect("couldn't get db connection from pool");
let user = get_user(&mut conn, uid)?; let uid = get_claims(req)?.uid;
let user = get_user(&mut conn, uid)?;
Ok(user.into()) Ok(user.into())
})() })())
)
} }
} }

View File

@@ -1,6 +1,10 @@
use actix_web::{
error::BlockingError,
http::{header::ContentType, StatusCode},
HttpResponse, ResponseError,
};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
use actix_web::{ResponseError, error::BlockingError, HttpResponse, http::{header::ContentType, StatusCode}};
use serde::{Serialize, Deserialize};
use validator::ValidationErrors; use validator::ValidationErrors;
use gamenight_database::error::DatabaseError; use gamenight_database::error::DatabaseError;
@@ -9,7 +13,7 @@ use gamenight_database::error::DatabaseError;
pub struct ApiError { pub struct ApiError {
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub status: u16, pub status: u16,
pub message: String pub message: String,
} }
impl Display for ApiError { impl Display for ApiError {
@@ -21,8 +25,8 @@ impl Display for ApiError {
impl ResponseError for ApiError { impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse { fn error_response(&self) -> HttpResponse {
HttpResponse::build(StatusCode::from_u16(self.status).unwrap()) HttpResponse::build(StatusCode::from_u16(self.status).unwrap())
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&self).unwrap()) .body(serde_json::to_string(&self).unwrap())
} }
} }
@@ -31,7 +35,7 @@ impl From<DatabaseError> for ApiError {
ApiError { ApiError {
//Todo, split this in unrecoverable and schema error //Todo, split this in unrecoverable and schema error
status: 500, status: 500,
message: value.0 message: value.0,
} }
} }
} }
@@ -40,7 +44,7 @@ impl From<BlockingError> for ApiError {
fn from(value: BlockingError) -> Self { fn from(value: BlockingError) -> Self {
ApiError { ApiError {
status: 500, status: 500,
message: value.to_string() message: value.to_string(),
} }
} }
} }
@@ -49,7 +53,7 @@ impl From<serde_json::Error> for ApiError {
fn from(value: serde_json::Error) -> Self { fn from(value: serde_json::Error) -> Self {
ApiError { ApiError {
status: 500, status: 500,
message: value.to_string() message: value.to_string(),
} }
} }
} }
@@ -58,7 +62,7 @@ impl From<jsonwebtoken::errors::Error> for ApiError {
fn from(value: jsonwebtoken::errors::Error) -> Self { fn from(value: jsonwebtoken::errors::Error) -> Self {
ApiError { ApiError {
status: 500, status: 500,
message: value.to_string() message: value.to_string(),
} }
} }
} }
@@ -67,7 +71,7 @@ impl From<ValidationErrors> for ApiError {
fn from(value: ValidationErrors) -> Self { fn from(value: ValidationErrors) -> Self {
ApiError { ApiError {
status: 422, status: 422,
message: value.to_string() message: value.to_string(),
} }
} }
} }
@@ -76,7 +80,7 @@ impl From<chrono::ParseError> for ApiError {
fn from(value: chrono::ParseError) -> Self { fn from(value: chrono::ParseError) -> Self {
ApiError { ApiError {
status: 422, status: 422,
message: value.to_string() message: value.to_string(),
} }
} }
} }
@@ -85,7 +89,7 @@ impl From<uuid::Error> for ApiError {
fn from(value: uuid::Error) -> Self { fn from(value: uuid::Error) -> Self {
ApiError { ApiError {
status: 422, status: 422,
message: value.to_string() message: value.to_string(),
} }
} }
} }

View File

@@ -1,53 +1,73 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder}; use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{game::{insert_game, load_game, rename_game}, owned_game::{disown_game, own_game, owned_games, OwnedGame}, DbPool, GetConnection}; use gamenight_database::{
game::{insert_game, load_game, rename_game},
owned_game::{disown_game, own_game, owned_games, OwnedGame},
DbPool, GetConnection,
};
use uuid::Uuid; use uuid::Uuid;
use crate::{models::{add_game_request_body::AddGameRequestBody, game::Game, game_id::GameId, rename_game_request_body::RenameGameRequestBody}, request::{authorization::AuthUser, error::ApiError}}; use crate::{
models::{
add_game_request_body::AddGameRequestBody, game::Game, game_id::GameId,
rename_game_request_body::RenameGameRequestBody,
},
request::{authorization::AuthUser, error::ApiError},
};
#[get("/games")] #[get("/games")]
pub async fn get_games(pool: web::Data<DbPool>, _user: AuthUser) -> Result<impl Responder, ApiError> { pub async fn get_games(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let games: Vec<gamenight_database::game::Game> = gamenight_database::games(&mut conn)?; let games: Vec<gamenight_database::game::Game> = gamenight_database::games(&mut conn)?;
let model: Vec<Game> = games.iter().map(|x| { let model: Vec<Game> = games
Game { .iter()
.map(|x| Game {
id: x.id.to_string(), id: x.id.to_string(),
name: x.name.clone(), name: x.name.clone(),
}} })
).collect(); .collect();
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&model)?) .body(serde_json::to_string(&model)?))
)
} }
impl From<AddGameRequestBody> for gamenight_database::game::Game { impl From<AddGameRequestBody> for gamenight_database::game::Game {
fn from(value: AddGameRequestBody) -> Self { fn from(value: AddGameRequestBody) -> Self {
Self { Self {
id: Uuid::new_v4(), id: Uuid::new_v4(),
name: value.name name: value.name,
} }
} }
} }
#[get("/game")] #[get("/game")]
pub async fn get_game(pool: web::Data<DbPool>, _user: AuthUser, game_id: web::Json<GameId>) -> Result<impl Responder, ApiError> { pub async fn get_game(
pool: web::Data<DbPool>,
_user: AuthUser,
game_id: web::Json<GameId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let db_game = load_game(&mut conn, Uuid::parse_str(&game_id.0.game_id)?)?; let db_game = load_game(&mut conn, Uuid::parse_str(&game_id.0.game_id)?)?;
let model = Game { let model = Game {
id: db_game.id.to_string(), id: db_game.id.to_string(),
name: db_game.name name: db_game.name,
}; };
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&model)?) .body(serde_json::to_string(&model)?))
)
} }
#[post("/game")] #[post("/game")]
pub async fn post_game(pool: web::Data<DbPool>, _user: AuthUser, game_data: web::Json<AddGameRequestBody>) -> Result<impl Responder, ApiError> { pub async fn post_game(
pool: web::Data<DbPool>,
_user: AuthUser,
game_data: web::Json<AddGameRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
insert_game(&mut conn, game_data.0.into())?; insert_game(&mut conn, game_data.0.into())?;
@@ -55,40 +75,68 @@ pub async fn post_game(pool: web::Data<DbPool>, _user: AuthUser, game_data: web:
} }
#[post("/rename_game")] #[post("/rename_game")]
pub async fn post_rename_game(pool: web::Data<DbPool>, _user: AuthUser, game_data: web::Json<RenameGameRequestBody>) -> Result <impl Responder, ApiError> { pub async fn post_rename_game(
pool: web::Data<DbPool>,
_user: AuthUser,
game_data: web::Json<RenameGameRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
rename_game(&mut conn, Uuid::parse_str(&game_data.0.id)?, game_data.0.name)?; rename_game(
&mut conn,
Uuid::parse_str(&game_data.0.id)?,
game_data.0.name,
)?;
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }
#[post("/own")] #[post("/own")]
pub async fn post_own_game(pool: web::Data<DbPool>, user: AuthUser, game_id: web::Json<GameId>) -> Result <impl Responder, ApiError> { pub async fn post_own_game(
pool: web::Data<DbPool>,
user: AuthUser,
game_id: web::Json<GameId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
own_game(&mut conn, OwnedGame { user_id: user.0.id, game_id: Uuid::parse_str(&game_id.0.game_id)? })?; own_game(
&mut conn,
OwnedGame {
user_id: user.0.id,
game_id: Uuid::parse_str(&game_id.0.game_id)?,
},
)?;
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }
#[post("/disown")] #[post("/disown")]
pub async fn post_disown_game(pool: web::Data<DbPool>, user: AuthUser, game_id: web::Json<GameId>) -> Result <impl Responder, ApiError> { pub async fn post_disown_game(
pool: web::Data<DbPool>,
user: AuthUser,
game_id: web::Json<GameId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
disown_game(&mut conn, OwnedGame { user_id: user.0.id, game_id: Uuid::parse_str(&game_id.0.game_id)? })?; disown_game(
&mut conn,
OwnedGame {
user_id: user.0.id,
game_id: Uuid::parse_str(&game_id.0.game_id)?,
},
)?;
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }
#[get("/owned_games")] #[get("/owned_games")]
pub async fn get_owned_games(pool: web::Data<DbPool>, user: AuthUser) -> Result <impl Responder, ApiError> { pub async fn get_owned_games(
pool: web::Data<DbPool>,
user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let game_ids = owned_games(&mut conn, user.0.id)?; let game_ids = owned_games(&mut conn, user.0.id)?;
let model : Vec<String> = game_ids.iter().map(|x| { let model: Vec<String> = game_ids.iter().map(|x| x.to_string()).collect();
x.to_string()
}).collect();
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&model)?) .body(serde_json::to_string(&model)?))
)
} }

View File

@@ -1,12 +1,17 @@
use actix_web::{get, web, Responder, http::header::ContentType, HttpResponse, post}; use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use chrono::{DateTime, ParseError}; use chrono::{DateTime, ParseError};
use uuid::Uuid; use uuid::Uuid;
use gamenight_database::{gamenight, DbPool, GetConnection}; use gamenight_database::{gamenight, DbPool, GetConnection};
use crate::{models::{add_gamenight_request_body::AddGamenightRequestBody, gamenight::Gamenight, get_gamenight_request_body::GetGamenightRequestBody}, request::authorization::AuthUser};
use crate::request::error::ApiError; use crate::request::error::ApiError;
use crate::{
models::{
add_gamenight_request_body::AddGamenightRequestBody, gamenight::Gamenight,
get_gamenight_request_body::GetGamenightRequestBody,
},
request::authorization::AuthUser,
};
impl AddGamenightRequestBody { impl AddGamenightRequestBody {
pub fn into_with_user(&self, user: AuthUser) -> Result<gamenight::Gamenight, ParseError> { pub fn into_with_user(&self, user: AuthUser) -> Result<gamenight::Gamenight, ParseError> {
@@ -14,7 +19,8 @@ impl AddGamenightRequestBody {
datetime: DateTime::parse_from_rfc3339(&self.datetime)?.with_timezone(&chrono::Utc), datetime: DateTime::parse_from_rfc3339(&self.datetime)?.with_timezone(&chrono::Utc),
id: Uuid::new_v4(), id: Uuid::new_v4(),
name: self.name.clone(), name: self.name.clone(),
owner_id: user.0.id owner_id: user.0.id,
location_id: None,
}) })
} }
} }
@@ -26,26 +32,33 @@ impl From<GetGamenightRequestBody> for Uuid {
} }
#[get("/gamenights")] #[get("/gamenights")]
pub async fn gamenights(pool: web::Data<DbPool>, _user: AuthUser) -> Result<impl Responder, ApiError> { pub async fn gamenights(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let gamenights: Vec<gamenight::Gamenight> = gamenight_database::gamenights(&mut conn)?; let gamenights: Vec<gamenight::Gamenight> = gamenight_database::gamenights(&mut conn)?;
let model: Vec<Gamenight> = gamenights.iter().map(|x| { let model: Vec<Gamenight> = gamenights
Gamenight { .iter()
.map(|x| Gamenight {
id: x.id.to_string(), id: x.id.to_string(),
name: x.name.clone(), name: x.name.clone(),
datetime: x.datetime.to_rfc3339(), datetime: x.datetime.to_rfc3339(),
owner_id: x.owner_id.to_string() owner_id: x.owner_id.to_string(),
}} })
).collect(); .collect();
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&model)?) .body(serde_json::to_string(&model)?))
)
} }
#[post("/gamenight")] #[post("/gamenight")]
pub async fn gamenight_post(pool: web::Data<DbPool>, user: AuthUser, gamenight_data: web::Json<AddGamenightRequestBody>) -> Result<impl Responder, ApiError> { pub async fn gamenight_post(
pool: web::Data<DbPool>,
user: AuthUser,
gamenight_data: web::Json<AddGamenightRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
gamenight::add_gamenight(&mut conn, gamenight_data.into_with_user(user)?)?; gamenight::add_gamenight(&mut conn, gamenight_data.into_with_user(user)?)?;
@@ -54,10 +67,14 @@ pub async fn gamenight_post(pool: web::Data<DbPool>, user: AuthUser, gamenight_d
} }
#[get("/gamenight")] #[get("/gamenight")]
pub async fn gamenight_get(pool: web::Data<DbPool>, _user: AuthUser, gamenight_data: web::Json<GetGamenightRequestBody>) -> Result<impl Responder, ApiError> { pub async fn gamenight_get(
pool: web::Data<DbPool>,
_user: AuthUser,
gamenight_data: web::Json<GetGamenightRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let gamenight = gamenight::get_gamenight(&mut conn, gamenight_data.into_inner().into())?; let gamenight = gamenight::get_gamenight(&mut conn, gamenight_data.into_inner().into())?;
let model = Gamenight{ let model = Gamenight {
id: gamenight.id.to_string(), id: gamenight.id.to_string(),
datetime: gamenight.datetime.to_rfc3339(), datetime: gamenight.datetime.to_rfc3339(),
name: gamenight.name, name: gamenight.name,

View File

@@ -1,34 +1,55 @@
use actix_web::{post, web, HttpResponse, Responder}; use actix_web::{post, web, HttpResponse, Responder};
use gamenight_database::{gamenight_participants::{delete_gamenight_participant, insert_gamenight_participant, GamenightParticipant}, DbPool, GetConnection}; use gamenight_database::{
gamenight_participants::{
delete_gamenight_participant, insert_gamenight_participant, GamenightParticipant,
},
DbPool, GetConnection,
};
use uuid::Uuid; use uuid::Uuid;
use crate::{models::gamenight_id::GamenightId, request::{authorization::AuthUser, error::ApiError}}; use crate::{
models::gamenight_id::GamenightId,
request::{authorization::AuthUser, error::ApiError},
};
#[post("/join")] #[post("/join")]
pub async fn post_join_gamenight(pool: web::Data<DbPool>, user: AuthUser, gamenight_id: web::Json<GamenightId>) -> Result<impl Responder, ApiError> { pub async fn post_join_gamenight(
pool: web::Data<DbPool>,
user: AuthUser,
gamenight_id: web::Json<GamenightId>,
) -> Result<impl Responder, ApiError> {
web::block(move || -> Result<usize, ApiError> { web::block(move || -> Result<usize, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
Ok(insert_gamenight_participant(&mut conn, GamenightParticipant { Ok(insert_gamenight_participant(
gamenight_id: Uuid::parse_str(&gamenight_id.gamenight_id)?, &mut conn,
user_id: user.0.id GamenightParticipant {
})?) gamenight_id: Uuid::parse_str(&gamenight_id.gamenight_id)?,
}).await??; user_id: user.0.id,
},
)?)
})
.await??;
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }
#[post("/leave")] #[post("/leave")]
pub async fn post_leave_gamenight(pool: web::Data<DbPool>, user: AuthUser, gamenight_id: web::Json<GamenightId>) -> Result<impl Responder, ApiError> { pub async fn post_leave_gamenight(
pool: web::Data<DbPool>,
user: AuthUser,
gamenight_id: web::Json<GamenightId>,
) -> Result<impl Responder, ApiError> {
web::block(move || -> Result<usize, ApiError> { web::block(move || -> Result<usize, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let participant = GamenightParticipant { let participant = GamenightParticipant {
gamenight_id: Uuid::parse_str(&gamenight_id.gamenight_id)?, gamenight_id: Uuid::parse_str(&gamenight_id.gamenight_id)?,
user_id: user.0.id user_id: user.0.id,
}; };
let x = delete_gamenight_participant(&mut conn, participant)?; let x = delete_gamenight_participant(&mut conn, participant)?;
Ok(x) Ok(x)
}).await??; })
.await??;
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }

View File

@@ -0,0 +1,65 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{
location::{insert_location, locations},
DbPool, GetConnection,
};
use uuid::Uuid;
use crate::{
models::{
add_location_request_body::AddLocationRequestBody, location::Location,
location_id::LocationId,
},
request::{authorization::AuthUser, error::ApiError},
};
impl From<AddLocationRequestBody> for gamenight_database::location::Location {
fn from(value: AddLocationRequestBody) -> Self {
Self {
id: Uuid::new_v4(),
name: value.name,
address: value.address,
note: value.note,
}
}
}
#[get("/locations")]
pub async fn get_locations(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let games: Vec<gamenight_database::location::Location> = locations(&mut conn)?;
let model: Vec<Location> = games
.iter()
.map(|x| Location {
id: x.id.to_string(),
name: x.name.clone(),
address: x.address.clone(),
note: x.note.clone(),
})
.collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}
#[post("/location")]
pub async fn post_location(
pool: web::Data<DbPool>,
_user: AuthUser,
game_data: web::Json<AddLocationRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let uuid = insert_location(&mut conn, game_data.0.into())?;
let model = LocationId {
location_id: uuid.to_string(),
};
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -0,0 +1,79 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{
location_owner::{grant_permission, location_permissions, revoke_permission, LocationOwner},
user::Role,
DbPool, GetConnection,
};
use uuid::Uuid;
use crate::{
models::{
authorize_location_request_body::{
AuthorizeLocationRequestBody,
Op::{Grant, Revoke},
},
location_id::LocationId,
user_id::UserId,
},
request::{authorization::AuthUser, error::ApiError},
};
impl<'a> TryFrom<&'a AuthorizeLocationRequestBody> for LocationOwner {
type Error = ApiError;
fn try_from(value: &'a AuthorizeLocationRequestBody) -> Result<Self, Self::Error> {
Ok(LocationOwner {
location_id: Uuid::parse_str(&value.location_id)?,
user_id: Uuid::parse_str(&value.user_id)?,
})
}
}
#[post("/location_authorize")]
pub async fn post_location_authorize(
pool: web::Data<DbPool>,
user: AuthUser,
auth_data: web::Json<AuthorizeLocationRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let inner_auth_data = &auth_data.into_inner();
let location_owner: LocationOwner = inner_auth_data.try_into()?;
let authorized = location_permissions(&mut conn, location_owner.location_id)?;
if user.0.role != Role::Admin && !authorized.contains(&user.0.id) {
Ok(HttpResponse::Unauthorized())
} else {
match inner_auth_data.op {
Grant => grant_permission(&mut conn, location_owner)?,
Revoke => revoke_permission(&mut conn, location_owner)?,
};
Ok(HttpResponse::Ok())
}
}
impl From<Uuid> for UserId {
fn from(value: Uuid) -> Self {
Self {
user_id: value.to_string(),
}
}
}
#[get("/authorized_location_user_ids")]
pub async fn get_authorized_location_user_ids(
pool: web::Data<DbPool>,
_user: AuthUser,
location_id: web::Json<LocationId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let permissions =
location_permissions(&mut conn, Uuid::parse_str(&location_id.0.location_id)?)?;
let model: Vec<UserId> = permissions.into_iter().map(Into::into).collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -1,27 +1,33 @@
mod user_handlers;
mod gamenight_handlers;
mod error;
mod authorization; mod authorization;
mod join_gamenight; mod error;
mod participant_handlers;
mod game; mod game;
mod gamenight_handlers;
mod join_gamenight;
mod location;
mod location_owner;
mod participant_handlers;
mod user_handlers;
pub use game::get_game;
pub use game::get_games;
pub use game::get_owned_games;
pub use game::post_disown_game;
pub use game::post_game;
pub use game::post_own_game;
pub use game::post_rename_game;
pub use gamenight_handlers::gamenight_get;
pub use gamenight_handlers::gamenight_post;
pub use gamenight_handlers::gamenights;
pub use join_gamenight::post_join_gamenight;
pub use join_gamenight::post_leave_gamenight;
pub use location::get_locations;
pub use location::post_location;
pub use location_owner::get_authorized_location_user_ids;
pub use location_owner::post_location_authorize;
pub use participant_handlers::get_get_participants;
pub use user_handlers::get_user;
pub use user_handlers::get_user_unauthenticated;
pub use user_handlers::get_users;
pub use user_handlers::login; pub use user_handlers::login;
pub use user_handlers::refresh; pub use user_handlers::refresh;
pub use user_handlers::register; pub use user_handlers::register;
pub use gamenight_handlers::gamenights;
pub use gamenight_handlers::gamenight_post;
pub use gamenight_handlers::gamenight_get;
pub use user_handlers::get_user;
pub use user_handlers::get_user_unauthenticated;
pub use join_gamenight::post_join_gamenight;
pub use join_gamenight::post_leave_gamenight;
pub use participant_handlers::get_get_participants;
pub use game::get_games;
pub use game::get_game;
pub use game::post_game;
pub use game::post_rename_game;
pub use game::post_own_game;
pub use game::post_disown_game;
pub use game::get_owned_games;

View File

@@ -2,16 +2,30 @@ use actix_web::{get, http::header::ContentType, web, HttpResponse, Responder};
use gamenight_database::{DbPool, GetConnection}; use gamenight_database::{DbPool, GetConnection};
use uuid::Uuid; use uuid::Uuid;
use crate::{models::{gamenight_id::GamenightId, participants::Participants}, request::{authorization::AuthUser, error::ApiError}}; use crate::{
models::{gamenight_id::GamenightId, participants::Participants},
request::{authorization::AuthUser, error::ApiError},
};
#[get("/participants")] #[get("/participants")]
pub async fn get_get_participants(pool: web::Data<DbPool>, _user: AuthUser, gamenight_info: web::Json<GamenightId>) -> Result<impl Responder, ApiError> { pub async fn get_get_participants(
pool: web::Data<DbPool>,
_user: AuthUser,
gamenight_info: web::Json<GamenightId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let users = gamenight_database::get_participants(&mut conn, &Uuid::parse_str(&gamenight_info.into_inner().gamenight_id)?)? let users = gamenight_database::get_participants(
.iter().map(|x| x.to_string()).collect(); &mut conn,
&Uuid::parse_str(&gamenight_info.into_inner().gamenight_id)?,
)?
.iter()
.map(|x| x.to_string())
.collect();
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&Participants{participants: users})?)) .body(serde_json::to_string(&Participants {
participants: users,
})?))
} }

View File

@@ -1,19 +1,14 @@
use crate::models::{
use actix_web::http::header::ContentType; login::Login, registration::Registration, token::Token, user::User, user_id::UserId,
use actix_web::{get, post, web, HttpResponse, Responder}; };
use crate::request::{authorization::get_token, error::ApiError};
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::user::{count_users_with_email, count_users_with_username}; use gamenight_database::user::{count_users_with_email, count_users_with_username};
use gamenight_database::{DbPool, GetConnection};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json;
use uuid::Uuid; use uuid::Uuid;
use validator::{Validate, ValidateArgs, ValidationError}; use validator::{Validate, ValidateArgs, ValidationError};
use crate::models::login::Login;
use crate::models::registration::Registration;
use crate::models::token::Token;
use crate::models::user::User;
use crate::models::user_id::UserId;
use crate::request::error::ApiError;
use crate::request::authorization::get_token;
use serde_json;
use gamenight_database::{DbPool, GetConnection};
use super::authorization::AuthUser; use super::authorization::AuthUser;
@@ -21,7 +16,7 @@ impl From<Login> for gamenight_database::user::LoginUser {
fn from(val: Login) -> Self { fn from(val: Login) -> Self {
gamenight_database::user::LoginUser { gamenight_database::user::LoginUser {
username: val.username, username: val.username,
password: val.password password: val.password,
} }
} }
} }
@@ -31,20 +26,22 @@ impl From<Registration> for gamenight_database::user::Register {
gamenight_database::user::Register { gamenight_database::user::Register {
email: val.email, email: val.email,
username: val.username, username: val.username,
password: val.password password: val.password,
} }
} }
} }
pub struct RegisterContext<'v_a> { pub struct RegisterContext<'v_a> {
pub pool: &'v_a DbPool pub pool: &'v_a DbPool,
} }
pub fn unique_username(username: &String, context: &RegisterContext) -> Result<(), ValidationError> { pub fn unique_username(
username: &String,
context: &RegisterContext,
) -> Result<(), ValidationError> {
let mut conn = context.pool.get_conn(); let mut conn = context.pool.get_conn();
match count_users_with_username(&mut conn, username) match count_users_with_username(&mut conn, username) {
{
Ok(0) => Ok(()), Ok(0) => Ok(()),
Ok(_) => Err(ValidationError::new("User already exists")), Ok(_) => Err(ValidationError::new("User already exists")),
Err(_) => Err(ValidationError::new("Database error while validating user")), Err(_) => Err(ValidationError::new("Database error while validating user")),
@@ -54,28 +51,23 @@ pub fn unique_username(username: &String, context: &RegisterContext) -> Result<(
pub fn unique_email(email: &String, context: &RegisterContext) -> Result<(), ValidationError> { pub fn unique_email(email: &String, context: &RegisterContext) -> Result<(), ValidationError> {
let mut conn = context.pool.get_conn(); let mut conn = context.pool.get_conn();
match count_users_with_email(&mut conn, email) match count_users_with_email(&mut conn, email) {
{
Ok(0) => Ok(()), Ok(0) => Ok(()),
Ok(_) => Err(ValidationError::new("email already exists")), Ok(_) => Err(ValidationError::new("email already exists")),
Err(_) => Err(ValidationError::new("Database error while validating email")) Err(_) => Err(ValidationError::new(
"Database error while validating email",
)),
} }
} }
#[derive(Serialize, Deserialize, Clone, Validate)] #[derive(Serialize, Deserialize, Clone, Validate)]
#[validate(context = RegisterContext::<'v_a>)] #[validate(context = RegisterContext::<'v_a>)]
pub struct ValidatableRegistration { pub struct ValidatableRegistration {
#[validate( #[validate(length(min = 1), custom(function = "unique_username", use_context))]
length(min = 1),
custom(function = "unique_username", use_context)
)]
pub username: String, pub username: String,
#[validate( #[validate(email, custom(function = "unique_email", use_context))]
email,
custom(function = "unique_email", use_context)
)]
pub email: String, pub email: String,
#[validate(length(min = 10), must_match(other = "password_repeat", ))] #[validate(length(min = 10), must_match(other = "password_repeat",))]
pub password: String, pub password: String,
pub password_repeat: String, pub password_repeat: String,
} }
@@ -86,13 +78,16 @@ impl From<Registration> for ValidatableRegistration {
username: value.username, username: value.username,
email: value.email, email: value.email,
password: value.password, password: value.password,
password_repeat: value.password_repeat password_repeat: value.password_repeat,
} }
} }
} }
#[get("/token")] #[get("/token")]
pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Result<impl Responder, ApiError> { pub async fn login(
pool: web::Data<DbPool>,
login_data: web::Json<Login>,
) -> Result<impl Responder, ApiError> {
let data = login_data.into_inner(); let data = login_data.into_inner();
if let Ok(Some(user)) = web::block(move || { if let Ok(Some(user)) = web::block(move || {
@@ -102,35 +97,45 @@ pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Res
.await? .await?
{ {
let token = get_token(&user)?; let token = get_token(&user)?;
let response = Token{ jwt_token: Some(token) }; let response = Token {
jwt_token: Some(token),
};
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&response)?)) .body(serde_json::to_string(&response)?))
} } else {
else { Err(ApiError {
Err(ApiError{status: 401, message: "User doesn't exist or password doesn't match".to_string()}) status: 401,
message: "User doesn't exist or password doesn't match".to_string(),
})
} }
} }
#[post("/token")] #[post("/token")]
pub async fn refresh(user: AuthUser) -> Result<impl Responder, ApiError> { pub async fn refresh(user: AuthUser) -> Result<impl Responder, ApiError> {
let new_token = get_token(&user.0)?; let new_token = get_token(&user.0)?;
let response = Token{ jwt_token: Some(new_token) }; let response = Token {
jwt_token: Some(new_token),
};
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
.body(serde_json::to_string(&response)?)) .body(serde_json::to_string(&response)?))
} }
#[post("/user")] #[post("/user")]
pub async fn register(pool: web::Data<DbPool>, register_data: web::Json<Registration>) -> Result<impl Responder, ApiError> { pub async fn register(
pool: web::Data<DbPool>,
register_data: web::Json<Registration>,
) -> Result<impl Responder, ApiError> {
web::block(move || -> Result<(), ApiError> { web::block(move || -> Result<(), ApiError> {
let validatable_registration: ValidatableRegistration = register_data.clone().into(); let validatable_registration: ValidatableRegistration = register_data.clone().into();
validatable_registration.validate_with_args(&RegisterContext{pool: &pool})?; validatable_registration.validate_with_args(&RegisterContext { pool: &pool })?;
let register_request = register_data.into_inner().into(); let register_request = register_data.into_inner().into();
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
gamenight_database::register(&mut conn, register_request)?; gamenight_database::register(&mut conn, register_request)?;
Ok(()) Ok(())
}).await??; })
.await??;
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }
@@ -146,10 +151,17 @@ impl From<gamenight_database::user::User> for User {
} }
#[get("/user")] #[get("/user")]
pub async fn get_user(pool: web::Data<DbPool>, _user: AuthUser, user_info: web::Json<UserId>) -> Result<impl Responder, ApiError> { pub async fn get_user(
pool: web::Data<DbPool>,
_user: AuthUser,
user_info: web::Json<UserId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn(); let mut conn = pool.get_conn();
let user = gamenight_database::user::get_user(&mut conn, Uuid::parse_str(&user_info.into_inner().user_id)?)?; let user = gamenight_database::user::get_user(
&mut conn,
Uuid::parse_str(&user_info.into_inner().user_id)?,
)?;
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(ContentType::json()) .content_type(ContentType::json())
@@ -157,9 +169,24 @@ pub async fn get_user(pool: web::Data<DbPool>, _user: AuthUser, user_info: web::
} }
#[get("/user")] #[get("/user")]
pub async fn get_user_unauthenticated(_path: web::Path<UserId>) -> Result<impl Responder, ApiError> { pub async fn get_user_unauthenticated(
_path: web::Path<UserId>,
) -> Result<impl Responder, ApiError> {
Ok(HttpResponse::Forbidden()) Ok(HttpResponse::Forbidden())
} }
#[get("/users")]
pub async fn get_users(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let users = gamenight_database::user::get_users(&mut conn)?;
let model: Vec<User> = users.into_iter().map(Into::into).collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -4,6 +4,8 @@ Cargo.toml
README.md README.md
docs/AddGameRequestBody.md docs/AddGameRequestBody.md
docs/AddGamenightRequestBody.md docs/AddGamenightRequestBody.md
docs/AddLocationRequestBody.md
docs/AuthorizeLocationRequestBody.md
docs/DefaultApi.md docs/DefaultApi.md
docs/Failure.md docs/Failure.md
docs/Game.md docs/Game.md
@@ -11,6 +13,8 @@ docs/GameId.md
docs/Gamenight.md docs/Gamenight.md
docs/GamenightId.md docs/GamenightId.md
docs/GetGamenightRequestBody.md docs/GetGamenightRequestBody.md
docs/Location.md
docs/LocationId.md
docs/Login.md docs/Login.md
docs/Participants.md docs/Participants.md
docs/Registration.md docs/Registration.md
@@ -25,12 +29,16 @@ src/apis/mod.rs
src/lib.rs src/lib.rs
src/models/add_game_request_body.rs src/models/add_game_request_body.rs
src/models/add_gamenight_request_body.rs src/models/add_gamenight_request_body.rs
src/models/add_location_request_body.rs
src/models/authorize_location_request_body.rs
src/models/failure.rs src/models/failure.rs
src/models/game.rs src/models/game.rs
src/models/game_id.rs src/models/game_id.rs
src/models/gamenight.rs src/models/gamenight.rs
src/models/gamenight_id.rs src/models/gamenight_id.rs
src/models/get_gamenight_request_body.rs src/models/get_gamenight_request_body.rs
src/models/location.rs
src/models/location_id.rs
src/models/login.rs src/models/login.rs
src/models/mod.rs src/models/mod.rs
src/models/participants.rs src/models/participants.rs

View File

@@ -1 +1 @@
7.14.0 7.17.0

View File

@@ -12,3 +12,8 @@ serde_json = "^1.0"
serde_repr = "^0.1" serde_repr = "^0.1"
url = "^2.5" url = "^2.5"
reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] } reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] }
[features]
default = ["native-tls"]
native-tls = ["reqwest/native-tls"]
rustls-tls = ["reqwest/rustls-tls"]

View File

@@ -10,7 +10,7 @@ This API client was generated by the [OpenAPI Generator](https://openapi-generat
- API version: 1.0 - API version: 1.0
- Package version: 0.1.0 - Package version: 0.1.0
- Generator version: 7.14.0 - Generator version: 7.17.0
- Build package: `org.openapitools.codegen.languages.RustClientCodegen` - Build package: `org.openapitools.codegen.languages.RustClientCodegen`
## Installation ## Installation
@@ -27,6 +27,7 @@ All URIs are relative to *http://localhost:8080*
Class | Method | HTTP request | Description Class | Method | HTTP request | Description
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
*DefaultApi* | [**authorized_location_user_ids_get**](docs/DefaultApi.md#authorized_location_user_ids_get) | **GET** /authorized_location_user_ids |
*DefaultApi* | [**disown_post**](docs/DefaultApi.md#disown_post) | **POST** /disown | *DefaultApi* | [**disown_post**](docs/DefaultApi.md#disown_post) | **POST** /disown |
*DefaultApi* | [**game_get**](docs/DefaultApi.md#game_get) | **GET** /game | *DefaultApi* | [**game_get**](docs/DefaultApi.md#game_get) | **GET** /game |
*DefaultApi* | [**game_post**](docs/DefaultApi.md#game_post) | **POST** /game | *DefaultApi* | [**game_post**](docs/DefaultApi.md#game_post) | **POST** /game |
@@ -36,6 +37,10 @@ Class | Method | HTTP request | Description
*DefaultApi* | [**get_token**](docs/DefaultApi.md#get_token) | **GET** /token | *DefaultApi* | [**get_token**](docs/DefaultApi.md#get_token) | **GET** /token |
*DefaultApi* | [**join_post**](docs/DefaultApi.md#join_post) | **POST** /join | *DefaultApi* | [**join_post**](docs/DefaultApi.md#join_post) | **POST** /join |
*DefaultApi* | [**leave_post**](docs/DefaultApi.md#leave_post) | **POST** /leave | *DefaultApi* | [**leave_post**](docs/DefaultApi.md#leave_post) | **POST** /leave |
*DefaultApi* | [**location_authorize_post**](docs/DefaultApi.md#location_authorize_post) | **POST** /location_authorize |
*DefaultApi* | [**location_get**](docs/DefaultApi.md#location_get) | **GET** /location |
*DefaultApi* | [**location_post**](docs/DefaultApi.md#location_post) | **POST** /location |
*DefaultApi* | [**locations_get**](docs/DefaultApi.md#locations_get) | **GET** /locations |
*DefaultApi* | [**own_post**](docs/DefaultApi.md#own_post) | **POST** /own | *DefaultApi* | [**own_post**](docs/DefaultApi.md#own_post) | **POST** /own |
*DefaultApi* | [**owned_games_get**](docs/DefaultApi.md#owned_games_get) | **GET** /owned_games | *DefaultApi* | [**owned_games_get**](docs/DefaultApi.md#owned_games_get) | **GET** /owned_games |
*DefaultApi* | [**participants_get**](docs/DefaultApi.md#participants_get) | **GET** /participants | Get all participants for a gamenight *DefaultApi* | [**participants_get**](docs/DefaultApi.md#participants_get) | **GET** /participants | Get all participants for a gamenight
@@ -44,18 +49,23 @@ Class | Method | HTTP request | Description
*DefaultApi* | [**post_token**](docs/DefaultApi.md#post_token) | **POST** /token | *DefaultApi* | [**post_token**](docs/DefaultApi.md#post_token) | **POST** /token |
*DefaultApi* | [**rename_game_post**](docs/DefaultApi.md#rename_game_post) | **POST** /rename_game | *DefaultApi* | [**rename_game_post**](docs/DefaultApi.md#rename_game_post) | **POST** /rename_game |
*DefaultApi* | [**user_get**](docs/DefaultApi.md#user_get) | **GET** /user | *DefaultApi* | [**user_get**](docs/DefaultApi.md#user_get) | **GET** /user |
*DefaultApi* | [**users_get**](docs/DefaultApi.md#users_get) | **GET** /users |
## Documentation For Models ## Documentation For Models
- [AddGameRequestBody](docs/AddGameRequestBody.md) - [AddGameRequestBody](docs/AddGameRequestBody.md)
- [AddGamenightRequestBody](docs/AddGamenightRequestBody.md) - [AddGamenightRequestBody](docs/AddGamenightRequestBody.md)
- [AddLocationRequestBody](docs/AddLocationRequestBody.md)
- [AuthorizeLocationRequestBody](docs/AuthorizeLocationRequestBody.md)
- [Failure](docs/Failure.md) - [Failure](docs/Failure.md)
- [Game](docs/Game.md) - [Game](docs/Game.md)
- [GameId](docs/GameId.md) - [GameId](docs/GameId.md)
- [Gamenight](docs/Gamenight.md) - [Gamenight](docs/Gamenight.md)
- [GamenightId](docs/GamenightId.md) - [GamenightId](docs/GamenightId.md)
- [GetGamenightRequestBody](docs/GetGamenightRequestBody.md) - [GetGamenightRequestBody](docs/GetGamenightRequestBody.md)
- [Location](docs/Location.md)
- [LocationId](docs/LocationId.md)
- [Login](docs/Login.md) - [Login](docs/Login.md)
- [Participants](docs/Participants.md) - [Participants](docs/Participants.md)
- [Registration](docs/Registration.md) - [Registration](docs/Registration.md)

View File

@@ -0,0 +1,13 @@
# AddLocation
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
**address** | Option<**String**> | | [optional]
**note** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,13 @@
# AddLocationRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
**address** | Option<**String**> | | [optional]
**note** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,13 @@
# AuthorizeLocationRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**location_id** | **String** | |
**user_id** | **String** | |
**op** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -4,6 +4,7 @@ All URIs are relative to *http://localhost:8080*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------
[**authorized_location_user_ids_get**](DefaultApi.md#authorized_location_user_ids_get) | **GET** /authorized_location_user_ids |
[**disown_post**](DefaultApi.md#disown_post) | **POST** /disown | [**disown_post**](DefaultApi.md#disown_post) | **POST** /disown |
[**game_get**](DefaultApi.md#game_get) | **GET** /game | [**game_get**](DefaultApi.md#game_get) | **GET** /game |
[**game_post**](DefaultApi.md#game_post) | **POST** /game | [**game_post**](DefaultApi.md#game_post) | **POST** /game |
@@ -13,6 +14,10 @@ Method | HTTP request | Description
[**get_token**](DefaultApi.md#get_token) | **GET** /token | [**get_token**](DefaultApi.md#get_token) | **GET** /token |
[**join_post**](DefaultApi.md#join_post) | **POST** /join | [**join_post**](DefaultApi.md#join_post) | **POST** /join |
[**leave_post**](DefaultApi.md#leave_post) | **POST** /leave | [**leave_post**](DefaultApi.md#leave_post) | **POST** /leave |
[**location_authorize_post**](DefaultApi.md#location_authorize_post) | **POST** /location_authorize |
[**location_get**](DefaultApi.md#location_get) | **GET** /location |
[**location_post**](DefaultApi.md#location_post) | **POST** /location |
[**locations_get**](DefaultApi.md#locations_get) | **GET** /locations |
[**own_post**](DefaultApi.md#own_post) | **POST** /own | [**own_post**](DefaultApi.md#own_post) | **POST** /own |
[**owned_games_get**](DefaultApi.md#owned_games_get) | **GET** /owned_games | [**owned_games_get**](DefaultApi.md#owned_games_get) | **GET** /owned_games |
[**participants_get**](DefaultApi.md#participants_get) | **GET** /participants | Get all participants for a gamenight [**participants_get**](DefaultApi.md#participants_get) | **GET** /participants | Get all participants for a gamenight
@@ -21,9 +26,38 @@ Method | HTTP request | Description
[**post_token**](DefaultApi.md#post_token) | **POST** /token | [**post_token**](DefaultApi.md#post_token) | **POST** /token |
[**rename_game_post**](DefaultApi.md#rename_game_post) | **POST** /rename_game | [**rename_game_post**](DefaultApi.md#rename_game_post) | **POST** /rename_game |
[**user_get**](DefaultApi.md#user_get) | **GET** /user | [**user_get**](DefaultApi.md#user_get) | **GET** /user |
[**users_get**](DefaultApi.md#users_get) | **GET** /users |
## authorized_location_user_ids_get
> Vec<models::UserId> authorized_location_user_ids_get(location_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**location_id** | Option<[**LocationId**](LocationId.md)> | | |
### Return type
[**Vec<models::UserId>**](UserId.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## disown_post ## disown_post
> disown_post(game_id) > disown_post(game_id)
@@ -274,6 +308,115 @@ Name | Type | Description | Required | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## location_authorize_post
> location_authorize_post(authorize_location_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**authorize_location_request_body** | Option<[**AuthorizeLocationRequestBody**](AuthorizeLocationRequestBody.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## location_get
> models::Location location_get(location_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**location_id** | Option<[**LocationId**](LocationId.md)> | | |
### Return type
[**models::Location**](Location.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## location_post
> models::LocationId location_post(add_location_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**add_location_request_body** | Option<[**AddLocationRequestBody**](AddLocationRequestBody.md)> | | |
### Return type
[**models::LocationId**](LocationId.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## locations_get
> Vec<models::Location> locations_get()
### Parameters
This endpoint does not need any parameter.
### Return type
[**Vec<models::Location>**](Location.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## own_post ## own_post
> own_post(game_id) > own_post(game_id)
@@ -504,3 +647,28 @@ Name | Type | Description | Required | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## users_get
> Vec<models::User> users_get()
### Parameters
This endpoint does not need any parameter.
### Return type
[**Vec<models::User>**](User.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

View File

@@ -0,0 +1,14 @@
# Location
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**name** | **String** | |
**address** | Option<**String**> | | [optional]
**note** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# LocationId
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**location_id** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -15,6 +15,15 @@ use crate::{apis::ResponseContent, models};
use super::{Error, configuration, ContentType}; use super::{Error, configuration, ContentType};
/// struct for typed errors of method [`authorized_location_user_ids_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AuthorizedLocationUserIdsGetError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`disown_post`] /// struct for typed errors of method [`disown_post`]
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
@@ -95,6 +104,42 @@ pub enum LeavePostError {
UnknownValue(serde_json::Value), UnknownValue(serde_json::Value),
} }
/// struct for typed errors of method [`location_authorize_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LocationAuthorizePostError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`location_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LocationGetError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`location_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LocationPostError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`locations_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LocationsGetError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`own_post`] /// struct for typed errors of method [`own_post`]
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
@@ -166,10 +211,59 @@ pub enum UserGetError {
UnknownValue(serde_json::Value), UnknownValue(serde_json::Value),
} }
/// struct for typed errors of method [`users_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UsersGetError {
Status400(models::Failure),
Status401(models::Failure),
UnknownValue(serde_json::Value),
}
pub async fn authorized_location_user_ids_get(configuration: &configuration::Configuration, location_id: Option<models::LocationId>) -> Result<Vec<models::UserId>, Error<AuthorizedLocationUserIdsGetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_location_id = location_id;
let uri_str = format!("{}/authorized_location_user_ids", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_location_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::UserId&gt;`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::UserId&gt;`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<AuthorizedLocationUserIdsGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn disown_post(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<(), Error<DisownPostError>> { pub async fn disown_post(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<(), Error<DisownPostError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_game_id = game_id; let p_body_game_id = game_id;
let uri_str = format!("{}/disown", configuration.base_path); let uri_str = format!("{}/disown", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -180,7 +274,7 @@ pub async fn disown_post(configuration: &configuration::Configuration, game_id:
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_game_id); req_builder = req_builder.json(&p_body_game_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -198,7 +292,7 @@ pub async fn disown_post(configuration: &configuration::Configuration, game_id:
pub async fn game_get(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<models::Game, Error<GameGetError>> { pub async fn game_get(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<models::Game, Error<GameGetError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_game_id = game_id; let p_body_game_id = game_id;
let uri_str = format!("{}/game", configuration.base_path); let uri_str = format!("{}/game", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
@@ -209,7 +303,7 @@ pub async fn game_get(configuration: &configuration::Configuration, game_id: Opt
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_game_id); req_builder = req_builder.json(&p_body_game_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -238,7 +332,7 @@ pub async fn game_get(configuration: &configuration::Configuration, game_id: Opt
pub async fn game_post(configuration: &configuration::Configuration, add_game_request_body: Option<models::AddGameRequestBody>) -> Result<(), Error<GamePostError>> { pub async fn game_post(configuration: &configuration::Configuration, add_game_request_body: Option<models::AddGameRequestBody>) -> Result<(), Error<GamePostError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_add_game_request_body = add_game_request_body; let p_body_add_game_request_body = add_game_request_body;
let uri_str = format!("{}/game", configuration.base_path); let uri_str = format!("{}/game", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -249,7 +343,7 @@ pub async fn game_post(configuration: &configuration::Configuration, add_game_re
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_add_game_request_body); req_builder = req_builder.json(&p_body_add_game_request_body);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -304,7 +398,7 @@ pub async fn games_get(configuration: &configuration::Configuration, ) -> Result
pub async fn get_gamenight(configuration: &configuration::Configuration, get_gamenight_request_body: Option<models::GetGamenightRequestBody>) -> Result<models::Gamenight, Error<GetGamenightError>> { pub async fn get_gamenight(configuration: &configuration::Configuration, get_gamenight_request_body: Option<models::GetGamenightRequestBody>) -> Result<models::Gamenight, Error<GetGamenightError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_get_gamenight_request_body = get_gamenight_request_body; let p_body_get_gamenight_request_body = get_gamenight_request_body;
let uri_str = format!("{}/gamenight", configuration.base_path); let uri_str = format!("{}/gamenight", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
@@ -315,7 +409,7 @@ pub async fn get_gamenight(configuration: &configuration::Configuration, get_gam
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_get_gamenight_request_body); req_builder = req_builder.json(&p_body_get_gamenight_request_body);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -383,7 +477,7 @@ pub async fn get_gamenights(configuration: &configuration::Configuration, ) -> R
/// Submit your credentials to get a JWT-token to use with the rest of the api. /// Submit your credentials to get a JWT-token to use with the rest of the api.
pub async fn get_token(configuration: &configuration::Configuration, login: Option<models::Login>) -> Result<models::Token, Error<GetTokenError>> { pub async fn get_token(configuration: &configuration::Configuration, login: Option<models::Login>) -> Result<models::Token, Error<GetTokenError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_login = login; let p_body_login = login;
let uri_str = format!("{}/token", configuration.base_path); let uri_str = format!("{}/token", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
@@ -391,7 +485,7 @@ pub async fn get_token(configuration: &configuration::Configuration, login: Opti
if let Some(ref user_agent) = configuration.user_agent { if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
} }
req_builder = req_builder.json(&p_login); req_builder = req_builder.json(&p_body_login);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -420,7 +514,7 @@ pub async fn get_token(configuration: &configuration::Configuration, login: Opti
pub async fn join_post(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<(), Error<JoinPostError>> { pub async fn join_post(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<(), Error<JoinPostError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_gamenight_id = gamenight_id; let p_body_gamenight_id = gamenight_id;
let uri_str = format!("{}/join", configuration.base_path); let uri_str = format!("{}/join", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -431,7 +525,7 @@ pub async fn join_post(configuration: &configuration::Configuration, gamenight_i
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_gamenight_id); req_builder = req_builder.json(&p_body_gamenight_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -449,7 +543,7 @@ pub async fn join_post(configuration: &configuration::Configuration, gamenight_i
pub async fn leave_post(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<(), Error<LeavePostError>> { pub async fn leave_post(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<(), Error<LeavePostError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_gamenight_id = gamenight_id; let p_body_gamenight_id = gamenight_id;
let uri_str = format!("{}/leave", configuration.base_path); let uri_str = format!("{}/leave", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -460,7 +554,7 @@ pub async fn leave_post(configuration: &configuration::Configuration, gamenight_
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_gamenight_id); req_builder = req_builder.json(&p_body_gamenight_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -476,9 +570,155 @@ pub async fn leave_post(configuration: &configuration::Configuration, gamenight_
} }
} }
pub async fn location_authorize_post(configuration: &configuration::Configuration, authorize_location_request_body: Option<models::AuthorizeLocationRequestBody>) -> Result<(), Error<LocationAuthorizePostError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_authorize_location_request_body = authorize_location_request_body;
let uri_str = format!("{}/location_authorize", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_authorize_location_request_body);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<LocationAuthorizePostError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn location_get(configuration: &configuration::Configuration, location_id: Option<models::LocationId>) -> Result<models::Location, Error<LocationGetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_location_id = location_id;
let uri_str = format!("{}/location", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_location_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Location`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Location`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<LocationGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn location_post(configuration: &configuration::Configuration, add_location_request_body: Option<models::AddLocationRequestBody>) -> Result<models::LocationId, Error<LocationPostError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_add_location_request_body = add_location_request_body;
let uri_str = format!("{}/location", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_add_location_request_body);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LocationId`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::LocationId`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<LocationPostError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn locations_get(configuration: &configuration::Configuration, ) -> Result<Vec<models::Location>, Error<LocationsGetError>> {
let uri_str = format!("{}/locations", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::Location&gt;`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::Location&gt;`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<LocationsGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn own_post(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<(), Error<OwnPostError>> { pub async fn own_post(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<(), Error<OwnPostError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_game_id = game_id; let p_body_game_id = game_id;
let uri_str = format!("{}/own", configuration.base_path); let uri_str = format!("{}/own", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -489,7 +729,7 @@ pub async fn own_post(configuration: &configuration::Configuration, game_id: Opt
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_game_id); req_builder = req_builder.json(&p_body_game_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -507,7 +747,7 @@ pub async fn own_post(configuration: &configuration::Configuration, game_id: Opt
pub async fn owned_games_get(configuration: &configuration::Configuration, user_id: Option<models::UserId>) -> Result<Vec<String>, Error<OwnedGamesGetError>> { pub async fn owned_games_get(configuration: &configuration::Configuration, user_id: Option<models::UserId>) -> Result<Vec<String>, Error<OwnedGamesGetError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_user_id = user_id; let p_body_user_id = user_id;
let uri_str = format!("{}/owned_games", configuration.base_path); let uri_str = format!("{}/owned_games", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
@@ -518,7 +758,7 @@ pub async fn owned_games_get(configuration: &configuration::Configuration, user_
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_user_id); req_builder = req_builder.json(&p_body_user_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -548,7 +788,7 @@ pub async fn owned_games_get(configuration: &configuration::Configuration, user_
/// Retrieve the participants of a single gamenight by id. /// Retrieve the participants of a single gamenight by id.
pub async fn participants_get(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<models::Participants, Error<ParticipantsGetError>> { pub async fn participants_get(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<models::Participants, Error<ParticipantsGetError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_gamenight_id = gamenight_id; let p_body_gamenight_id = gamenight_id;
let uri_str = format!("{}/participants", configuration.base_path); let uri_str = format!("{}/participants", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
@@ -559,7 +799,7 @@ pub async fn participants_get(configuration: &configuration::Configuration, game
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_gamenight_id); req_builder = req_builder.json(&p_body_gamenight_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -589,7 +829,7 @@ pub async fn participants_get(configuration: &configuration::Configuration, game
/// Add a gamenight by providing a name and a date, only available when providing an JWT token. /// Add a gamenight by providing a name and a date, only available when providing an JWT token.
pub async fn post_gamenight(configuration: &configuration::Configuration, add_gamenight_request_body: Option<models::AddGamenightRequestBody>) -> Result<(), Error<PostGamenightError>> { pub async fn post_gamenight(configuration: &configuration::Configuration, add_gamenight_request_body: Option<models::AddGamenightRequestBody>) -> Result<(), Error<PostGamenightError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_add_gamenight_request_body = add_gamenight_request_body; let p_body_add_gamenight_request_body = add_gamenight_request_body;
let uri_str = format!("{}/gamenight", configuration.base_path); let uri_str = format!("{}/gamenight", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -600,7 +840,7 @@ pub async fn post_gamenight(configuration: &configuration::Configuration, add_ga
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_add_gamenight_request_body); req_builder = req_builder.json(&p_body_add_gamenight_request_body);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -619,7 +859,7 @@ pub async fn post_gamenight(configuration: &configuration::Configuration, add_ga
/// Create a new user given a registration token and user information, username and email must be unique, and password and password_repeat must match. /// Create a new user given a registration token and user information, username and email must be unique, and password and password_repeat must match.
pub async fn post_register(configuration: &configuration::Configuration, registration: Option<models::Registration>) -> Result<(), Error<PostRegisterError>> { pub async fn post_register(configuration: &configuration::Configuration, registration: Option<models::Registration>) -> Result<(), Error<PostRegisterError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_registration = registration; let p_body_registration = registration;
let uri_str = format!("{}/user", configuration.base_path); let uri_str = format!("{}/user", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -630,7 +870,7 @@ pub async fn post_register(configuration: &configuration::Configuration, registr
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_registration); req_builder = req_builder.json(&p_body_registration);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -686,7 +926,7 @@ pub async fn post_token(configuration: &configuration::Configuration, ) -> Resul
pub async fn rename_game_post(configuration: &configuration::Configuration, rename_game_request_body: Option<models::RenameGameRequestBody>) -> Result<(), Error<RenameGamePostError>> { pub async fn rename_game_post(configuration: &configuration::Configuration, rename_game_request_body: Option<models::RenameGameRequestBody>) -> Result<(), Error<RenameGamePostError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_rename_game_request_body = rename_game_request_body; let p_body_rename_game_request_body = rename_game_request_body;
let uri_str = format!("{}/rename_game", configuration.base_path); let uri_str = format!("{}/rename_game", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
@@ -697,7 +937,7 @@ pub async fn rename_game_post(configuration: &configuration::Configuration, rena
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_rename_game_request_body); req_builder = req_builder.json(&p_body_rename_game_request_body);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -716,7 +956,7 @@ pub async fn rename_game_post(configuration: &configuration::Configuration, rena
/// Get a user from primary id /// Get a user from primary id
pub async fn user_get(configuration: &configuration::Configuration, user_id: Option<models::UserId>) -> Result<models::User, Error<UserGetError>> { pub async fn user_get(configuration: &configuration::Configuration, user_id: Option<models::UserId>) -> Result<models::User, Error<UserGetError>> {
// add a prefix to parameters to efficiently prevent name collisions // add a prefix to parameters to efficiently prevent name collisions
let p_user_id = user_id; let p_body_user_id = user_id;
let uri_str = format!("{}/user", configuration.base_path); let uri_str = format!("{}/user", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
@@ -727,7 +967,7 @@ pub async fn user_get(configuration: &configuration::Configuration, user_id: Opt
if let Some(ref token) = configuration.bearer_access_token { if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned()); req_builder = req_builder.bearer_auth(token.to_owned());
}; };
req_builder = req_builder.json(&p_user_id); req_builder = req_builder.json(&p_body_user_id);
let req = req_builder.build()?; let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?; let resp = configuration.client.execute(req).await?;
@@ -754,3 +994,40 @@ pub async fn user_get(configuration: &configuration::Configuration, user_id: Opt
} }
} }
pub async fn users_get(configuration: &configuration::Configuration, ) -> Result<Vec<models::User>, Error<UsersGetError>> {
let uri_str = format!("{}/users", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::User&gt;`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::User&gt;`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UsersGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}

View File

@@ -0,0 +1,33 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddLocation {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "address", skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(rename = "note", skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
impl AddLocation {
pub fn new(name: String) -> AddLocation {
AddLocation {
name,
address: None,
note: None,
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddLocationRequestBody {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "address", skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(rename = "note", skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
impl AddLocationRequestBody {
pub fn new(name: String) -> AddLocationRequestBody {
AddLocationRequestBody {
name,
address: None,
note: None,
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AuthorizeLocationRequestBody {
#[serde(rename = "location_id")]
pub location_id: String,
#[serde(rename = "user_id")]
pub user_id: String,
#[serde(rename = "op")]
pub op: Op,
}
impl AuthorizeLocationRequestBody {
pub fn new(location_id: String, user_id: String, op: Op) -> AuthorizeLocationRequestBody {
AuthorizeLocationRequestBody {
location_id,
user_id,
op,
}
}
}
///
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Op {
#[serde(rename = "grant")]
Grant,
#[serde(rename = "revoke")]
Revoke,
}
impl Default for Op {
fn default() -> Op {
Self::Grant
}
}

View File

@@ -0,0 +1,36 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Location {
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "address", skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(rename = "note", skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
impl Location {
pub fn new(id: String, name: String) -> Location {
Location {
id,
name,
address: None,
note: None,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct LocationId {
#[serde(rename = "location_id")]
pub location_id: String,
}
impl LocationId {
pub fn new(location_id: String) -> LocationId {
LocationId {
location_id,
}
}
}

View File

@@ -2,6 +2,10 @@ pub mod add_game_request_body;
pub use self::add_game_request_body::AddGameRequestBody; pub use self::add_game_request_body::AddGameRequestBody;
pub mod add_gamenight_request_body; pub mod add_gamenight_request_body;
pub use self::add_gamenight_request_body::AddGamenightRequestBody; pub use self::add_gamenight_request_body::AddGamenightRequestBody;
pub mod add_location_request_body;
pub use self::add_location_request_body::AddLocationRequestBody;
pub mod authorize_location_request_body;
pub use self::authorize_location_request_body::AuthorizeLocationRequestBody;
pub mod failure; pub mod failure;
pub use self::failure::Failure; pub use self::failure::Failure;
pub mod game; pub mod game;
@@ -14,6 +18,10 @@ pub mod gamenight_id;
pub use self::gamenight_id::GamenightId; pub use self::gamenight_id::GamenightId;
pub mod get_gamenight_request_body; pub mod get_gamenight_request_body;
pub use self::get_gamenight_request_body::GetGamenightRequestBody; pub use self::get_gamenight_request_body::GetGamenightRequestBody;
pub mod location;
pub use self::location::Location;
pub mod location_id;
pub use self::location_id::LocationId;
pub mod login; pub mod login;
pub use self::login::Login; pub use self::login::Login;
pub mod participants; pub mod participants;

828
gamenight-cli/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ edition = "2024"
[dependencies] [dependencies]
gamenight-api-client-rs = { path = "../gamenight-api-client-rs" } gamenight-api-client-rs = { path = "../gamenight-api-client-rs" }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
inquire = { version = "0.7.5", features = ["date"] } inquire = { version = "0.7.5", features = ["date", "editor"] }
async-trait = "0.1" async-trait = "0.1"
dyn-clone = "1.0" dyn-clone = "1.0"
chrono = "0.4" chrono = "0.4"

View File

@@ -42,6 +42,9 @@ pub struct Config {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)] #[serde(default)]
pub last_instance: Option<String>, pub last_instance: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub editor: Option<String>,
} }
impl Config { impl Config {
@@ -57,7 +60,8 @@ impl Config {
pub fn new() -> Config { pub fn new() -> Config {
Config { Config {
instances: vec![], instances: vec![],
last_instance: None last_instance: None,
editor: None
} }
} }

View File

@@ -0,0 +1,31 @@
use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
#[derive(Clone)]
pub struct Location {
pub id: Uuid,
pub name: String,
pub address: Option<String>,
pub note: Option<String>,
}
impl From<models::Location> for Location {
fn from(location: models::Location) -> Self {
Self {
id: Uuid::parse_str(&location.id).unwrap(),
name: location.name,
address: location.address,
note: location.note
}
}
}
impl Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"name: {}
address: {}
note: {}"#, &self.name, &<std::option::Option<std::string::String> as Clone>::clone(&self.address).unwrap_or_default(), &<std::option::Option<std::string::String> as Clone>::clone(&self.note).unwrap_or_default())
}
}

View File

@@ -4,3 +4,4 @@ pub mod config;
pub mod participants; pub mod participants;
pub mod game; pub mod game;
pub mod owned_games; pub mod owned_games;
pub mod location;

View File

@@ -0,0 +1,65 @@
use std::{ffi::OsStr, fmt::Display};
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::{location_authorize_post, location_post}, models::{authorize_location_request_body::Op::Grant, AddLocationRequestBody, AuthorizeLocationRequestBody}};
use inquire::{Editor, Text};
use super::*;
#[derive(Clone)]
pub struct AddLocation {
}
impl AddLocation {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for AddLocation {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
if let Some(name) = Text::new("What is the name of the location you want to add?").prompt_skippable()? {
let address;
let note;
{
address = Text::new("Optional: What is the address?").prompt_skippable()?;
let mut editor_prompt = Editor::new("What is the name of the location you want to add?");
let editor_command;
if let Some(editor) = &state.gamenight_configuration.editor {
editor_command = editor.clone();
editor_prompt = editor_prompt.with_editor_command(OsStr::new(&editor_command))
}
note = editor_prompt.prompt_skippable()?;
}
let add_location_request = AddLocationRequestBody {
name,
address,
note
};
let location_id = location_post(&state.api_configuration, Some(add_location_request)).await?;
let add_authorize_request = AuthorizeLocationRequestBody {
location_id: location_id.location_id.to_string(),
user_id: state.get_user_id()?.to_string(),
op: Grant
};
location_authorize_post(&state.api_configuration, Some(add_authorize_request)).await?;
}
Ok((FlowOutcome::Cancelled, state))
}
}
impl Display for AddLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Add Location")
}
}

View File

@@ -1,7 +1,7 @@
use inquire::{ui::RenderConfig, Select}; use inquire::{ui::RenderConfig, Select};
use crate::flows::{add_gamenight::AddGamenight, exit::Exit, games::Games, list_gamenights::ListGamenights}; use crate::flows::{add_gamenight::AddGamenight, exit::Exit, games::Games, list_gamenights::ListGamenights, locations::Locations};
use super::*; use super::*;
@@ -27,6 +27,7 @@ impl<'a> Flow<'a> for GamenightMenu {
Box::new(ListGamenights::new()), Box::new(ListGamenights::new()),
Box::new(AddGamenight::new()), Box::new(AddGamenight::new()),
Box::new(Games::new()), Box::new(Games::new()),
Box::new(Locations::new()),
Box::new(Exit::new()) Box::new(Exit::new())
]; ];

View File

@@ -0,0 +1,49 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::apis::default_api::locations_get;
use inquire::{ui::RenderConfig, Select};
use crate::flows::{exit::Exit, view_location::ViewLocation};
use super::*;
#[derive(Clone)]
pub struct ListLocations {
}
impl ListLocations {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for ListLocations {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let locations = locations_get(&state.api_configuration).await?;
let mut flows = locations.into_iter().map(|location| -> Box<dyn Flow + Send> {
Box::new(ViewLocation::new(location.into()))
}).collect::<Vec::<Box::<dyn Flow + Send>>>();
flows.push(Box::new(Exit::new()));
let choice = Select::new("What would you like to do?", flows)
.with_help_message("Select the action you want to take or quit the program")
.with_render_config(RenderConfig {
option_index_prefix: inquire::ui::IndexPrefix::Simple,
..Default::default()
})
.prompt_skippable()?;
self.continue_choice(state, &choice).await
}
}
impl Display for ListLocations {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "List all locations")
}
}

View File

@@ -0,0 +1,101 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::{self, authorized_location_user_ids_get, users_get}, models::{AuthorizeLocationRequestBody, LocationId, User}};
use inquire::MultiSelect;
use uuid::Uuid;
use crate::flows::FlowError;
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
#[derive(Clone)]
pub struct LocationAuthorize {
location_id: Uuid
}
impl LocationAuthorize {
pub fn new(location_id: Uuid) -> Self {
Self {
location_id
}
}
}
struct AuthorizeMultiSelectStruct<'a> {
id: Uuid,
username: &'a String
}
impl<'a> Display for AuthorizeMultiSelectStruct<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.username)
}
}
impl<'a> TryFrom<&'a User> for AuthorizeMultiSelectStruct<'a> {
type Error = FlowError;
fn try_from(value: &'a User) -> Result<Self, Self::Error> {
Ok(AuthorizeMultiSelectStruct{
id: Uuid::parse_str(&value.id)?,
username: &value.username
})
}
}
#[async_trait]
impl<'a> Flow<'a> for LocationAuthorize {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let users = users_get(&state.api_configuration).await?;
let location_id = LocationId {
location_id: self.location_id.to_string()
};
let authorized_user_ids = authorized_location_user_ids_get(&state.api_configuration, Some(location_id)).await?;
let authorized_user_ids : Vec<String> = authorized_user_ids.into_iter().map(|x| x.user_id).collect();
let options: Vec<AuthorizeMultiSelectStruct> = users.iter().map(|x| {x.try_into()}).collect::<Result<Vec<AuthorizeMultiSelectStruct>, FlowError>>()?;
let (authorized_indices, authorized_users) : &(Vec<usize>, Vec<&User>) = &users.iter().enumerate().filter(|t| {
authorized_user_ids.contains(&t.1.id)
}).unzip();
let selections = MultiSelect::new("Which users should be able to host gamenights in this location?", options)
.with_default(&authorized_indices[..])
.prompt_skippable()?;
if let Some(selections) = &selections {
for selection in selections {
if authorized_users.iter().find(|x| {x.id == selection.id.to_string()}).is_none() {
default_api::location_authorize_post(&state.api_configuration, Some(
AuthorizeLocationRequestBody {
location_id: self.location_id.to_string(),
user_id: selection.id.to_string(),
op: gamenight_api_client_rs::models::authorize_location_request_body::Op::Grant
}
)).await?
}
}
for authorized_user in authorized_users {
if selections.iter().find(|x| {x.id.to_string() == authorized_user.id}).is_none() {
default_api::location_authorize_post(&state.api_configuration, Some(
AuthorizeLocationRequestBody {
location_id: self.location_id.to_string(),
user_id: authorized_user.id.to_string(),
op: gamenight_api_client_rs::models::authorize_location_request_body::Op::Revoke
}
)).await?
}
}
}
clear_screen::clear();
Ok((FlowOutcome::Successful, state))
}
}
impl Display for LocationAuthorize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Authorize users for location")
}
}

View File

@@ -0,0 +1,46 @@
use std::fmt::Display;
use async_trait::async_trait;
use inquire::{ui::RenderConfig, Select};
use crate::flows::{add_location::AddLocation, exit::Exit, list_locations::ListLocations};
use super::*;
#[derive(Clone)]
pub struct Locations {
}
impl Locations {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for Locations {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let flows: Vec<Box<dyn Flow + Send>> = vec![
Box::new(ListLocations::new()),
Box::new(AddLocation::new()),
Box::new(Exit::new())
];
let choice = Select::new("What would you like to do?", flows)
.with_help_message("Select the action you want to take or quit the program")
.with_render_config(RenderConfig {
option_index_prefix: inquire::ui::IndexPrefix::Simple,
..Default::default()
})
.prompt_skippable()?;
self.continue_choice(state, &choice).await
}
}
impl Display for Locations {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Locations")
}
}

View File

@@ -30,6 +30,11 @@ mod view_game;
mod rename_game; mod rename_game;
mod own; mod own;
mod disown; mod disown;
mod locations;
mod list_locations;
mod view_location;
mod add_location;
mod location_authorize;
pub struct GamenightState { pub struct GamenightState {
api_configuration: Configuration, api_configuration: Configuration,
@@ -122,6 +127,14 @@ impl From<ParseIntError> for FlowError {
} }
} }
impl From<jsonwebtoken::errors::Error> for FlowError {
fn from(value: jsonwebtoken::errors::Error) -> Self {
Self {
error: value.to_string()
}
}
}
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum FlowOutcome { pub enum FlowOutcome {
Successful, Successful,

View File

@@ -23,14 +23,6 @@ impl ViewGamenight {
} }
} }
impl From<jsonwebtoken::errors::Error> for FlowError {
fn from(value: jsonwebtoken::errors::Error) -> Self {
Self {
error: value.to_string()
}
}
}
#[async_trait] #[async_trait]
impl<'a> Flow<'a> for ViewGamenight { impl<'a> Flow<'a> for ViewGamenight {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> { async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {

View File

@@ -0,0 +1,44 @@
use inquire::Select;
use crate::{domain::location::Location, flows::{exit::Exit, location_authorize::LocationAuthorize}};
use super::*;
#[derive(Clone)]
pub struct ViewLocation {
location: Location
}
impl ViewLocation {
pub fn new(location: Location) -> Self {
Self {
location
}
}
}
#[async_trait]
impl<'a> Flow<'a> for ViewLocation {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
println!("{}", self.location);
let options: Vec<Box<dyn Flow<'a> + Send>> = vec![
Box::new(LocationAuthorize::new(self.location.id)),
Box::new(Exit::new())
];
let choice = Select::new("What do you want to do:", options)
.prompt_skippable()?;
self.continue_choice(state, &choice).await
}
}
impl Display for ViewLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.location.name)
}
}

View File

@@ -0,0 +1,8 @@
ALTER TABLE gamenight
DROP CONSTRAINT FK_location_id,
DROP column location_id;
DROP TABLE location_owner;
DROP TABLE location;

View File

@@ -0,0 +1,18 @@
CREATE TABLE location (
id UUID NOT NULL PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL,
address VARCHAR,
note VARCHAR
);
CREATE TABLE location_owner (
location_id UUID NOT NULL,
user_id UUID NOT NULL,
CONSTRAINT FK_location_id FOREIGN KEY (location_id) REFERENCES location(id) ON DELETE CASCADE,
CONSTRAINT FK_user_id FOREIGN KEY (user_id) REFERENCES client(id) ON DELETE CASCADE,
PRIMARY KEY(location_id, user_id)
);
ALTER TABLE gamenight
ADD location_id UUID,
ADD CONSTRAINT FK_location_id FOREIGN KEY (location_id) REFERENCES location(id) ON DELETE SET NULL;

View File

@@ -1,7 +1,9 @@
use diesel::{dsl::insert_into, ExpressionMethods, Insertable, PgConnection, QueryDsl, Queryable, RunQueryDsl};
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use crate::schema::game; use crate::schema::game;
use diesel::{
ExpressionMethods, Insertable, PgConnection, QueryDsl, Queryable, RunQueryDsl, dsl::insert_into,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::error::DatabaseError; use super::error::DatabaseError;
@@ -12,7 +14,7 @@ pub struct Game {
pub name: String, pub name: String,
} }
pub fn games(conn: &mut PgConnection) -> Result<Vec::<Game>, DatabaseError> { pub fn games(conn: &mut PgConnection) -> Result<Vec<Game>, DatabaseError> {
Ok(game::table.load::<Game>(conn)?) Ok(game::table.load::<Game>(conn)?)
} }
@@ -24,7 +26,11 @@ pub fn insert_game(conn: &mut PgConnection, game: Game) -> Result<usize, Databas
Ok(insert_into(game::table).values(&game).execute(conn)?) Ok(insert_into(game::table).values(&game).execute(conn)?)
} }
pub fn rename_game(conn: &mut PgConnection, id: Uuid, name: String) -> Result<usize, DatabaseError> { pub fn rename_game(
conn: &mut PgConnection,
id: Uuid,
name: String,
) -> Result<usize, DatabaseError> {
Ok(diesel::update(game::table.filter(game::id.eq(id))) Ok(diesel::update(game::table.filter(game::id.eq(id)))
.set(game::name.eq(&name)) .set(game::name.eq(&name))
.execute(conn)?) .execute(conn)?)

View File

@@ -1,8 +1,8 @@
use chrono::{DateTime, Utc};
use diesel::{Insertable, Queryable, PgConnection, RunQueryDsl, insert_into, QueryDsl};
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use crate::schema::gamenight; use crate::schema::gamenight;
use chrono::{DateTime, Utc};
use diesel::{Insertable, PgConnection, QueryDsl, Queryable, RunQueryDsl, insert_into};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::error::DatabaseError; use super::error::DatabaseError;
@@ -13,14 +13,20 @@ pub struct Gamenight {
pub name: String, pub name: String,
pub datetime: DateTime<Utc>, pub datetime: DateTime<Utc>,
pub owner_id: Uuid, pub owner_id: Uuid,
pub location_id: Option<Uuid>,
} }
pub fn gamenights(conn: &mut PgConnection) -> Result<Vec::<Gamenight>, DatabaseError> { pub fn gamenights(conn: &mut PgConnection) -> Result<Vec<Gamenight>, DatabaseError> {
Ok(gamenight::table.load::<Gamenight>(conn)?) Ok(gamenight::table.load::<Gamenight>(conn)?)
} }
pub fn add_gamenight(conn: &mut PgConnection, gamenight: Gamenight) -> Result<usize, DatabaseError> { pub fn add_gamenight(
Ok(insert_into(gamenight::table).values(&gamenight).execute(conn)?) conn: &mut PgConnection,
gamenight: Gamenight,
) -> Result<usize, DatabaseError> {
Ok(insert_into(gamenight::table)
.values(&gamenight)
.execute(conn)?)
} }
pub fn get_gamenight(conn: &mut PgConnection, id: Uuid) -> Result<Gamenight, DatabaseError> { pub fn get_gamenight(conn: &mut PgConnection, id: Uuid) -> Result<Gamenight, DatabaseError> {

View File

@@ -1,11 +1,13 @@
use diesel::{BoolExpressionMethods, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl}; use diesel::{
use serde::{Serialize, Deserialize}; BoolExpressionMethods, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use crate::schema::gamenight_participant; use crate::schema::gamenight_participant;
use super::error::DatabaseError;
use super::DbConnection; use super::DbConnection;
use super::error::DatabaseError;
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)] #[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(belongs_to(Gamenight))] #[diesel(belongs_to(Gamenight))]
@@ -23,14 +25,23 @@ pub fn get_participants(conn: &mut DbConnection, id: &Uuid) -> Result<Vec<Uuid>,
.get_results(conn)?) .get_results(conn)?)
} }
pub fn insert_gamenight_participant(conn: &mut DbConnection, gp: GamenightParticipant) -> Result<usize, DatabaseError> { pub fn insert_gamenight_participant(
conn: &mut DbConnection,
gp: GamenightParticipant,
) -> Result<usize, DatabaseError> {
Ok(gp.insert_into(gamenight_participant::table).execute(conn)?) Ok(gp.insert_into(gamenight_participant::table).execute(conn)?)
} }
pub fn delete_gamenight_participant(conn: &mut DbConnection, gp: GamenightParticipant) -> Result<usize, DatabaseError> { pub fn delete_gamenight_participant(
Ok(diesel::delete(gamenight_participant::table conn: &mut DbConnection,
.filter(gamenight_participant::gamenight_id.eq(&gp.gamenight_id) gp: GamenightParticipant,
.and(gamenight_participant::user_id.eq(gp.user_id)))) ) -> Result<usize, DatabaseError> {
.execute(conn)?) Ok(diesel::delete(
gamenight_participant::table.filter(
gamenight_participant::gamenight_id
.eq(&gp.gamenight_id)
.and(gamenight_participant::user_id.eq(gp.user_id)),
),
)
.execute(conn)?)
} }

View File

@@ -1,25 +1,27 @@
pub mod user;
pub mod error; pub mod error;
pub mod schema; pub mod game;
pub mod gamenight; pub mod gamenight;
pub mod gamenight_participants; pub mod gamenight_participants;
pub mod game; pub mod location;
pub mod location_owner;
pub mod owned_game; pub mod owned_game;
pub mod schema;
pub mod user;
use diesel::PgConnection;
use diesel::r2d2::ConnectionManager; use diesel::r2d2::ConnectionManager;
use diesel::r2d2::ManageConnection; use diesel::r2d2::ManageConnection;
use diesel::r2d2::Pool; use diesel::r2d2::Pool;
use diesel::r2d2::PooledConnection; use diesel::r2d2::PooledConnection;
use diesel::PgConnection;
use diesel_migrations::embed_migrations;
use diesel_migrations::EmbeddedMigrations; use diesel_migrations::EmbeddedMigrations;
use diesel_migrations::embed_migrations;
use diesel_migrations::MigrationHarness; use diesel_migrations::MigrationHarness;
pub use user::login; pub use game::games;
pub use user::register;
pub use gamenight::gamenights; pub use gamenight::gamenights;
pub use gamenight_participants::get_participants; pub use gamenight_participants::get_participants;
pub use game::games; pub use user::login;
pub use user::register;
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
pub type DbConnection = PgConnection; pub type DbConnection = PgConnection;
@@ -38,7 +40,10 @@ pub fn get_connection_pool(url: &str) -> DbPool {
.build(manager) .build(manager)
.expect("Could not build connection pool") .expect("Could not build connection pool")
} }
pub trait GetConnection<T> where T: ManageConnection { pub trait GetConnection<T>
where
T: ManageConnection,
{
fn get_conn(&self) -> PooledConnection<T>; fn get_conn(&self) -> PooledConnection<T>;
} }

View File

@@ -0,0 +1,44 @@
use crate::schema::location::{self, id};
use diesel::{
ExpressionMethods, Insertable, PgConnection, QueryDsl, Queryable, RunQueryDsl, dsl::insert_into,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::error::DatabaseError;
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(table_name = location)]
pub struct Location {
pub id: Uuid,
pub name: String,
pub address: Option<String>,
pub note: Option<String>,
}
pub fn locations(conn: &mut PgConnection) -> Result<Vec<Location>, DatabaseError> {
Ok(location::table.load::<Location>(conn)?)
}
pub fn load_location(conn: &mut PgConnection, uuid: Uuid) -> Result<Location, DatabaseError> {
Ok(location::table.find(uuid).get_result(conn)?)
}
pub fn insert_location(conn: &mut PgConnection, location: Location) -> Result<Uuid, DatabaseError> {
Ok(insert_into(location::table)
.values(&location)
.returning(id)
.get_result(conn)?)
}
pub fn rename_location(
conn: &mut PgConnection,
uuid: Uuid,
name: String,
) -> Result<usize, DatabaseError> {
Ok(
diesel::update(location::table.filter(location::id.eq(uuid)))
.set(location::name.eq(&name))
.execute(conn)?,
)
}

View File

@@ -0,0 +1,47 @@
use crate::{DbConnection, schema::location_owner, user::DatabaseError};
use diesel::{
BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl,
dsl::{delete, insert_into},
prelude::{Insertable, Queryable},
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(table_name = location_owner)]
pub struct LocationOwner {
pub location_id: Uuid,
pub user_id: Uuid,
}
pub fn grant_permission(
conn: &mut DbConnection,
location_owner: LocationOwner,
) -> Result<usize, DatabaseError> {
Ok(insert_into(location_owner::table)
.values(&location_owner)
.execute(conn)?)
}
pub fn revoke_permission(
conn: &mut DbConnection,
location_owner: LocationOwner,
) -> Result<usize, DatabaseError> {
Ok(delete(location_owner::table)
.filter(
location_owner::location_id
.eq(location_owner.location_id)
.and(location_owner::user_id.eq(location_owner.user_id)),
)
.execute(conn)?)
}
pub fn location_permissions(
conn: &mut DbConnection,
location_id: Uuid,
) -> Result<Vec<Uuid>, DatabaseError> {
Ok(location_owner::table
.select(location_owner::user_id)
.filter(location_owner::location_id.eq(location_id))
.get_results(conn)?)
}

View File

@@ -1,28 +1,38 @@
use diesel::{dsl::{delete, insert_into}, prelude::{Insertable, Queryable}, BoolExpressionMethods, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use crate::{schema::owned_game, user::DatabaseError}; use crate::{schema::owned_game, user::DatabaseError};
use diesel::{
BoolExpressionMethods, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl,
dsl::{delete, insert_into},
prelude::{Insertable, Queryable},
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)] #[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(table_name = owned_game)] #[diesel(table_name = owned_game)]
pub struct OwnedGame { pub struct OwnedGame {
pub user_id: Uuid, pub user_id: Uuid,
pub game_id: Uuid pub game_id: Uuid,
} }
pub fn own_game(conn: &mut PgConnection, owned_game: OwnedGame) -> Result<usize, DatabaseError>{ pub fn own_game(conn: &mut PgConnection, owned_game: OwnedGame) -> Result<usize, DatabaseError> {
Ok(insert_into(owned_game::table).values(&owned_game).execute(conn)?) Ok(insert_into(owned_game::table)
.values(&owned_game)
.execute(conn)?)
} }
pub fn disown_game(conn: &mut PgConnection, owned_game: OwnedGame) -> Result<usize, DatabaseError> { pub fn disown_game(conn: &mut PgConnection, owned_game: OwnedGame) -> Result<usize, DatabaseError> {
Ok(delete(owned_game::table) Ok(delete(owned_game::table)
.filter(owned_game::user_id.eq(&owned_game.user_id) .filter(
.and(owned_game::game_id.eq(&owned_game.game_id)) owned_game::user_id
).execute(conn)?) .eq(&owned_game.user_id)
.and(owned_game::game_id.eq(&owned_game.game_id)),
)
.execute(conn)?)
} }
pub fn owned_games(conn: &mut PgConnection, uuid: Uuid) -> Result<Vec<Uuid>, DatabaseError> { pub fn owned_games(conn: &mut PgConnection, uuid: Uuid) -> Result<Vec<Uuid>, DatabaseError> {
Ok(owned_game::table.select(owned_game::game_id) Ok(owned_game::table
.select(owned_game::game_id)
.filter(owned_game::user_id.eq(uuid)) .filter(owned_game::user_id.eq(uuid))
.get_results(conn)?) .get_results(conn)?)
} }

View File

@@ -31,6 +31,7 @@ diesel::table! {
name -> Varchar, name -> Varchar,
datetime -> Timestamptz, datetime -> Timestamptz,
owner_id -> Uuid, owner_id -> Uuid,
location_id -> Nullable<Uuid>,
} }
} }
@@ -48,6 +49,22 @@ diesel::table! {
} }
} }
diesel::table! {
location (id) {
id -> Uuid,
name -> Varchar,
address -> Nullable<Varchar>,
note -> Nullable<Varchar>,
}
}
diesel::table! {
location_owner (location_id, user_id) {
location_id -> Uuid,
user_id -> Uuid,
}
}
diesel::table! { diesel::table! {
owned_game (user_id, game_id) { owned_game (user_id, game_id) {
user_id -> Uuid, user_id -> Uuid,
@@ -73,10 +90,13 @@ diesel::table! {
} }
diesel::joinable!(gamenight -> client (owner_id)); diesel::joinable!(gamenight -> client (owner_id));
diesel::joinable!(gamenight -> location (location_id));
diesel::joinable!(gamenight_gamelist -> game (game_id)); diesel::joinable!(gamenight_gamelist -> game (game_id));
diesel::joinable!(gamenight_gamelist -> gamenight (gamenight_id)); diesel::joinable!(gamenight_gamelist -> gamenight (gamenight_id));
diesel::joinable!(gamenight_participant -> client (user_id)); diesel::joinable!(gamenight_participant -> client (user_id));
diesel::joinable!(gamenight_participant -> gamenight (gamenight_id)); diesel::joinable!(gamenight_participant -> gamenight (gamenight_id));
diesel::joinable!(location_owner -> client (user_id));
diesel::joinable!(location_owner -> location (location_id));
diesel::joinable!(owned_game -> client (user_id)); diesel::joinable!(owned_game -> client (user_id));
diesel::joinable!(owned_game -> game (game_id)); diesel::joinable!(owned_game -> game (game_id));
diesel::joinable!(pwd -> client (user_id)); diesel::joinable!(pwd -> client (user_id));
@@ -87,6 +107,8 @@ diesel::allow_tables_to_appear_in_same_query!(
gamenight, gamenight,
gamenight_gamelist, gamenight_gamelist,
gamenight_participant, gamenight_participant,
location,
location_owner,
owned_game, owned_game,
pwd, pwd,
registration_tokens, registration_tokens,

View File

@@ -1,22 +1,20 @@
use argon2::password_hash::Salt; use crate::DbConnection;
use diesel::Connection; use argon2::Argon2;
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, Insertable, Queryable};
use diesel_derive_enum::DbEnum;
use argon2::password_hash::SaltString;
use argon2::PasswordHash; use argon2::PasswordHash;
use argon2::PasswordVerifier; use argon2::PasswordVerifier;
use argon2::Argon2;
use argon2::password_hash::PasswordHasher; use argon2::password_hash::PasswordHasher;
use argon2::password_hash::Salt;
use argon2::password_hash::SaltString;
use argon2::password_hash::rand_core::OsRng; use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::rand_core::RngCore; use argon2::password_hash::rand_core::RngCore;
use crate::DbConnection; use diesel::Connection;
use diesel::{ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl};
use diesel_derive_enum::DbEnum;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::schema::{pwd, client};
pub use super::error::DatabaseError; pub use super::error::DatabaseError;
use super::schema::{client, pwd};
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)] #[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(table_name = pwd)] #[diesel(table_name = pwd)]
@@ -38,12 +36,12 @@ pub struct User {
#[ExistingTypePath = "crate::schema::sql_types::Role"] #[ExistingTypePath = "crate::schema::sql_types::Role"]
pub enum Role { pub enum Role {
Admin, Admin,
User User,
} }
pub struct LoginUser { pub struct LoginUser {
pub username: String, pub username: String,
pub password: String pub password: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@@ -61,7 +59,7 @@ pub struct RegisterResult {
pub struct Register { pub struct Register {
pub username: String, pub username: String,
pub email: String, pub email: String,
pub password: String pub password: String,
} }
pub fn login(conn: &mut DbConnection, user: LoginUser) -> Result<Option<User>, DatabaseError> { pub fn login(conn: &mut DbConnection, user: LoginUser) -> Result<Option<User>, DatabaseError> {
@@ -125,16 +123,26 @@ pub fn register(conn: &mut DbConnection, register: Register) -> Result<(), Datab
}) })
} }
pub fn count_users_with_username(conn: &mut DbConnection, username: &String) -> Result<i64, DatabaseError> { pub fn count_users_with_username(
conn: &mut DbConnection,
username: &String,
) -> Result<i64, DatabaseError> {
Ok(client::table Ok(client::table
.count() .count()
.filter(client::username.eq(username)) .filter(client::username.eq(username))
.get_result::<i64>(conn)?) .get_result::<i64>(conn)?)
} }
pub fn count_users_with_email(conn: &mut DbConnection, email: &String) -> Result<i64, DatabaseError> { pub fn count_users_with_email(
conn: &mut DbConnection,
email: &String,
) -> Result<i64, DatabaseError> {
Ok(client::table Ok(client::table
.count() .count()
.filter(client::email.eq(email)) .filter(client::email.eq(email))
.get_result::<i64>(conn)?) .get_result::<i64>(conn)?)
} }
pub fn get_users(conn: &mut DbConnection) -> Result<Vec<User>, DatabaseError> {
Ok(client::table.load(conn)?)
}