Compare commits

...

2 commits

Author SHA1 Message Date
Timo Ley 9a7f992265 feat: client and person management 2023-06-24 21:49:12 +02:00
Timo Ley 7744a4c31e feat: added addPerson procedure 2023-06-24 21:37:52 +02:00
9 changed files with 256 additions and 22 deletions

View file

@ -43,6 +43,56 @@ BEGIN
return ids;
END;
CREATE OR REPLACE PROCEDURE addPerson(
pFirstName IN VARCHAR,
pLastName IN VARCHAR,
pAge IN NUMBER,
pStreet IN VARCHAR,
pHouseNumber IN NUMBER,
pPostalCode IN NUMBER,
pCity IN VARCHAR,
pCountry IN VARCHAR
)
IS
BEGIN
INSERT INTO ADDRESS
(
addressId,
street,
houseNumber,
postalCode,
city,
country
)
VALUES
(
ADDRESS_SEQ.nextval,
pStreet,
pHouseNumber,
pPostalCode,
pCity,
pCountry
);
INSERT INTO PERSON
(
personId,
name,
lastName,
age,
addressId
)
VALUES
(
PERSON_SEQ.nextval,
pFirstName,
pLastName,
pAge,
ADDRESS_SEQ.currval
);
COMMIT;
END;
CREATE OR REPLACE PROCEDURE rooms(clientz IN NUMBER, months IN NUMBER)
IS

View file

@ -11,7 +11,7 @@ pub struct Room {
pub accessibility: bool,
}
#[derive(Serialize)]
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Address {
pub street: String,
@ -85,4 +85,19 @@ pub struct BookingBody {
pub cost: i32,
pub pension_type: String,
pub client_id: i32,
}
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PersonBody {
pub first_name: String,
pub last_name: String,
pub age: i32,
pub address: Address,
}
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClientBody {
pub bank_details: String,
}

View file

@ -1,7 +1,7 @@
use axum::{Router, routing::BoxRoute, extract::{Extension, Path}, response::IntoResponse, Json, handler::get};
use hyper::StatusCode;
use crate::{Service, error::ServiceError};
use crate::{Service, error::ServiceError, model::{ClientBody, Client}};
async fn clients(
Extension(service): Extension<Service>,
@ -19,9 +19,33 @@ async fn client(
Ok(Json(client))
}
async fn set_client(
Json(payload): Json<ClientBody>,
Path(id): Path<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let person = service.get_person(id).await?
.ok_or_else(|| ServiceError::ErrorResponse(StatusCode::NOT_FOUND, Some("person does not exist".to_string())))?;
service.set_client(id, payload.clone()).await?;
let client = Client {
id,
bank_details: payload.bank_details,
person_data: person,
};
Ok(Json(client))
}
async fn delete_client(
Path(id): Path<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
service.delete_client(id).await?;
Ok(StatusCode::NO_CONTENT)
}
pub fn routes() -> Router<BoxRoute> {
Router::new()
.route("/", get(clients))
.route("/:id", get(client))
.route("/:id", get(client).put(set_client).delete(delete_client))
.boxed()
}

View file

@ -4,6 +4,7 @@ mod rooms;
mod clerks;
mod bookings;
mod clients;
mod persons;
pub fn routes() -> Router<BoxRoute> {
Router::new()
@ -11,5 +12,6 @@ pub fn routes() -> Router<BoxRoute> {
.nest("/bookings", bookings::routes())
.nest("/clerks", clerks::routes())
.nest("/clients", clients::routes())
.nest("/persons", persons::routes())
.boxed()
}

35
src/routes/persons.rs Normal file
View file

@ -0,0 +1,35 @@
use axum::{Router, routing::BoxRoute, extract::{Path, Extension}, response::IntoResponse, Json, handler::{get, post}};
use hyper::StatusCode;
use crate::{Service, error::ServiceError, model::{PersonBody, Person}};
async fn person(
Path(id): Path<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let person = service.get_person(id).await?
.ok_or_else(|| ServiceError::ErrorResponse(StatusCode::NOT_FOUND, Some("person not found".to_string())))?;
Ok(Json(person))
}
async fn add_person(
Json(payload): Json<PersonBody>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let id = service.add_person(payload.clone()).await?;
let person = Person {
id,
first_name: payload.first_name,
last_name: payload.last_name,
age: payload.age,
address: payload.address,
};
Ok((StatusCode::CREATED, Json(person)))
}
pub fn routes() -> Router<BoxRoute> {
Router::new()
.route("/", post(add_person))
.route("/:id", get(person))
.boxed()
}

View file

@ -1,6 +1,6 @@
use sibyl::Row;
use crate::{ServiceInner, model::{Person, Address, Clerk}, error::ServiceError};
use crate::{ServiceInner, model::Clerk, error::ServiceError};
impl ServiceInner {
@ -97,22 +97,6 @@ impl ServiceInner {
})
}
pub fn map_person_row(row: Row, offset: usize) -> Result<Person, ServiceError> {
Ok(Person {
id: row.get(0 + offset)?,
first_name: row.get(1 + offset)?,
last_name: row.get(2 + offset)?,
age: row.get(3 + offset)?,
address: Address {
street: row.get(4 + offset)?,
house_number: row.get(5 + offset)?,
postal_code: row.get(6 + offset)?,
city: row.get(7 + offset)?,
country: row.get(8 + offset)?,
}
})
}
pub fn map_clerk_row(row: Row) -> Result<Clerk, ServiceError> {
Ok(Clerk {
staff_number: row.get(0)?,

View file

@ -1,6 +1,6 @@
use sibyl::Row;
use crate::{ServiceInner, model::Client, error::ServiceError};
use crate::{ServiceInner, model::{Client, ClientBody}, error::ServiceError};
impl ServiceInner {
@ -64,6 +64,53 @@ impl ServiceInner {
})
}
pub async fn set_client(&self, id: i32, client: ClientBody) -> Result<(), ServiceError> {
let session = self.pool.get_session().await?;
let stmt = session.prepare("
UPDATE CLIENT SET
bankDetails = :BANK
WHERE personId = :ID
").await?;
let changes = stmt.execute((
(":ID", id),
(":BANK", client.bank_details.clone())
)).await?;
if changes == 0 {
let stmt = session.prepare("
INSERT INTO CLIENT
(
personId,
bankDetails
)
VALUES
(
:ID,
:BANK
)
").await?;
stmt.execute((
(":ID", id),
(":BANK", client.bank_details)
)).await?;
}
session.commit().await?;
Ok(())
}
pub async fn delete_client(&self, id: i32) -> Result<(), ServiceError> {
let session = self.pool.get_session().await?;
let stmt = session.prepare("
DELETE FROM CLIENT WHERE personId = :ID
").await?;
stmt.execute(id).await?;
session.commit().await?;
Ok(())
}
pub fn map_client_row(row: Row) -> Result<Client, ServiceError> {
Ok(Client {
id: row.get(1)?,

View file

@ -1,4 +1,5 @@
mod bookings;
mod rooms;
mod clerks;
mod clients;
mod clients;
mod persons;

76
src/sql/persons.rs Normal file
View file

@ -0,0 +1,76 @@
use hyper::StatusCode;
use sibyl::Row;
use crate::{ServiceInner, error::ServiceError, model::{PersonBody, Person, Address}};
impl ServiceInner {
pub async fn get_person(&self, id: i32) -> Result<Option<Person>, ServiceError> {
let session = self.pool.get_session().await?;
let stmt = session.prepare("
SELECT
p.personId,
p.name,
p.lastName,
p.age,
ad.street,
ad.houseNumber,
ad.postalCode,
ad.city,
ad.country
FROM Person p
INNER JOIN Address ad ON p.addressId = ad.addressId
WHERE p.personId = :ID
").await?;
let row = stmt.query_single(id).await?;
Ok(match row {
Some(row) => {
Some(Self::map_person_row(row, 0)?)
},
None => None,
})
}
pub async fn add_person(&self, person: PersonBody) -> Result<i32, ServiceError> {
let session = self.pool.get_session().await?;
let stmt = session.prepare("
BEGIN
addPerson(:FIRST, :LAST, :AGE, :STREET, :HOUSE, :POSTAL, :CITY, :COUNTRY);
END;
").await?;
stmt.execute((
(":FIRST", person.first_name),
(":LAST", person.last_name),
(":AGE", person.age),
(":STREET", person.address.street),
(":HOUSE", person.address.house_number),
(":POSTAL", person.address.postal_code),
(":CITY", person.address.city),
(":COUNTRY", person.address.country)
)).await?;
let stmt = session.prepare("SELECT PERSON_SEQ.currval FROM dual").await?;
let row = stmt.query_single("").await?
.ok_or_else(|| ServiceError::ErrorResponse(StatusCode::INTERNAL_SERVER_ERROR, None))?;
let id: i32 = row.get(0)?;
Ok(id)
}
pub fn map_person_row(row: Row, offset: usize) -> Result<Person, ServiceError> {
Ok(Person {
id: row.get(0 + offset)?,
first_name: row.get(1 + offset)?,
last_name: row.get(2 + offset)?,
age: row.get(3 + offset)?,
address: Address {
street: row.get(4 + offset)?,
house_number: row.get(5 + offset)?,
postal_code: row.get(6 + offset)?,
city: row.get(7 + offset)?,
country: row.get(8 + offset)?,
}
})
}
}