complete redesign
This commit is contained in:
156
server/src/adresar/docs/example.txt
Normal file
156
server/src/adresar/docs/example.txt
Normal file
@@ -0,0 +1,156 @@
|
||||
❯ grpcurl -plaintext -d '{"id": 1}' localhost:50051 multieko2.Adresar/GetAdresar
|
||||
{
|
||||
"id": "1",
|
||||
"firma": "Updated Firma",
|
||||
"kz": "Updated KZ",
|
||||
"drc": "Updated DRC",
|
||||
"ulica": "Updated Ulica",
|
||||
"psc": "Updated PSC",
|
||||
"mesto": "Updated Mesto",
|
||||
"stat": "Updated Stat",
|
||||
"banka": "Updated Banka",
|
||||
"ucet": "Updated Ucet",
|
||||
"skladm": "Updated Skladm",
|
||||
"ico": "Updated ICO",
|
||||
"kontakt": "Updated Kontakt",
|
||||
"telefon": "Updated Telefon",
|
||||
"skladu": "Updated Skladu",
|
||||
"fax": "Updated Fax"
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{"id": 2}' localhost:50051 multieko2.Adresar/GetAdresar
|
||||
{
|
||||
"id": "2",
|
||||
"firma": "asdfasf",
|
||||
"kz": " ",
|
||||
"drc": " ",
|
||||
"ulica": " ",
|
||||
"psc": "sdfasdf",
|
||||
"mesto": "asf",
|
||||
"stat": "as",
|
||||
"banka": "df",
|
||||
"ucet": "asf",
|
||||
"skladm": "f",
|
||||
"ico": "f",
|
||||
"kontakt": "f",
|
||||
"telefon": "f",
|
||||
"skladu": "f",
|
||||
"fax": " "
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{"id": 1}' localhost:50051 multieko2.Adresar/DeleteAdresar
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{"id": 1}' localhost:50051 multieko2.Adresar/GetAdresar
|
||||
ERROR:
|
||||
Code: NotFound
|
||||
Message: no rows returned by a query that expected to return at least one row
|
||||
❯ grpcurl -plaintext -d '{"id": 2}' localhost:50051 multieko2.Adresar/GetAdresar
|
||||
{
|
||||
"id": "2",
|
||||
"firma": "asdfasf",
|
||||
"kz": " ",
|
||||
"drc": " ",
|
||||
"ulica": " ",
|
||||
"psc": "sdfasdf",
|
||||
"mesto": "asf",
|
||||
"stat": "as",
|
||||
"banka": "df",
|
||||
"ucet": "asf",
|
||||
"skladm": "f",
|
||||
"ico": "f",
|
||||
"kontakt": "f",
|
||||
"telefon": "f",
|
||||
"skladu": "f",
|
||||
"fax": " "
|
||||
}
|
||||
|
||||
❯ grpcurl -plaintext -d '{
|
||||
"firma": "New Firma",
|
||||
"kz": "New KZ",
|
||||
"drc": "New DRC",
|
||||
"ulica": "New Ulica",
|
||||
"psc": "New PSC",
|
||||
"mesto": "New Mesto",
|
||||
"stat": "New Stat",
|
||||
"banka": "New Banka",
|
||||
"ucet": "New Ucet",
|
||||
"skladm": "New Skladm",
|
||||
"ico": "New ICO",
|
||||
"kontakt": "New Kontakt",
|
||||
"telefon": "New Telefon",
|
||||
"skladu": "New Skladu",
|
||||
"fax": "New Fax"
|
||||
}' localhost:50051 multieko2.Adresar/PostAdresar
|
||||
{
|
||||
"id": "43",
|
||||
"firma": "New Firma",
|
||||
"kz": "New KZ",
|
||||
"drc": "New DRC",
|
||||
"ulica": "New Ulica",
|
||||
"psc": "New PSC",
|
||||
"mesto": "New Mesto",
|
||||
"stat": "New Stat",
|
||||
"banka": "New Banka",
|
||||
"ucet": "New Ucet",
|
||||
"skladm": "New Skladm",
|
||||
"ico": "New ICO",
|
||||
"kontakt": "New Kontakt",
|
||||
"telefon": "New Telefon",
|
||||
"skladu": "New Skladu",
|
||||
"fax": "New Fax"
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{
|
||||
"id": 43,
|
||||
"firma": "Updated Firma",
|
||||
"kz": "Updated KZ",
|
||||
"drc": "Updated DRC",
|
||||
"ulica": "Updated Ulica",
|
||||
"psc": "Updated PSC",
|
||||
"mesto": "Updated Mesto",
|
||||
"stat": "Updated Stat",
|
||||
"banka": "Updated Banka",
|
||||
"ucet": "Updated Ucet",
|
||||
"skladm": "Updated Skladm",
|
||||
"ico": "Updated ICO",
|
||||
"kontakt": "Updated Kontakt",
|
||||
"telefon": "Updated Telefon",
|
||||
"skladu": "Updated Skladu",
|
||||
"fax": "Updated Fax"
|
||||
}' localhost:50051 multieko2.Adresar/PutAdresar
|
||||
{
|
||||
"id": "43",
|
||||
"firma": "Updated Firma",
|
||||
"kz": "Updated KZ",
|
||||
"drc": "Updated DRC",
|
||||
"ulica": "Updated Ulica",
|
||||
"psc": "Updated PSC",
|
||||
"mesto": "Updated Mesto",
|
||||
"stat": "Updated Stat",
|
||||
"banka": "Updated Banka",
|
||||
"ucet": "Updated Ucet",
|
||||
"skladm": "Updated Skladm",
|
||||
"ico": "Updated ICO",
|
||||
"kontakt": "Updated Kontakt",
|
||||
"telefon": "Updated Telefon",
|
||||
"skladu": "Updated Skladu",
|
||||
"fax": "Updated Fax"
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{"id": 43}' localhost:50051 multieko2.Adresar/GetAdresar
|
||||
{
|
||||
"id": "43",
|
||||
"firma": "Updated Firma",
|
||||
"kz": "Updated KZ",
|
||||
"drc": "Updated DRC",
|
||||
"ulica": "Updated Ulica",
|
||||
"psc": "Updated PSC",
|
||||
"mesto": "Updated Mesto",
|
||||
"stat": "Updated Stat",
|
||||
"banka": "Updated Banka",
|
||||
"ucet": "Updated Ucet",
|
||||
"skladm": "Updated Skladm",
|
||||
"ico": "Updated ICO",
|
||||
"kontakt": "Updated Kontakt",
|
||||
"telefon": "Updated Telefon",
|
||||
"skladu": "Updated Skladu",
|
||||
"fax": "Updated Fax"
|
||||
}
|
||||
33
server/src/adresar/docs/get_example.txt
Normal file
33
server/src/adresar/docs/get_example.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# TOTAL items in the adresar
|
||||
❯ grpcurl -plaintext localhost:50051 multieko2.Adresar/GetAdresarCount
|
||||
{
|
||||
"count": "43"
|
||||
}
|
||||
|
||||
# Item at this count. If there are 43 items, number 1 is the first item
|
||||
❯ grpcurl -plaintext -d '{"position": 1}' localhost:50051 multieko2.Adresar/GetAdresarByPosition
|
||||
{
|
||||
"id": "2",
|
||||
"firma": "asdfasf",
|
||||
"kz": " ",
|
||||
"drc": " ",
|
||||
"ulica": " ",
|
||||
"psc": "sdfasdf",
|
||||
"mesto": "asf",
|
||||
"stat": "as",
|
||||
"banka": "df",
|
||||
"ucet": "asf",
|
||||
"skladm": "f",
|
||||
"ico": "f",
|
||||
"kontakt": "f",
|
||||
"telefon": "f",
|
||||
"skladu": "f",
|
||||
"fax": " "
|
||||
}
|
||||
# Item fetched by id. The first item was created and removed, therefore number 1 in ids doenst exists, since first to exist now has number 2
|
||||
❯ grpcurl -plaintext -d '{"id": 1}' localhost:50051 multieko2.Adresar/GetAdresar
|
||||
ERROR:
|
||||
Code: NotFound
|
||||
Message: no rows returned by a query that expected to return at least one row
|
||||
╭─ ~ ············································· 69 ✘
|
||||
╰─
|
||||
15
server/src/adresar/handlers.rs
Normal file
15
server/src/adresar/handlers.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
// src/adresar/handlers.rs
|
||||
|
||||
pub mod post_adresar;
|
||||
pub mod get_adresar;
|
||||
pub mod put_adresar;
|
||||
pub mod delete_adresar;
|
||||
pub mod get_adresar_count;
|
||||
pub mod get_adresar_by_position;
|
||||
|
||||
pub use post_adresar::post_adresar;
|
||||
pub use get_adresar::get_adresar;
|
||||
pub use put_adresar::put_adresar;
|
||||
pub use delete_adresar::delete_adresar;
|
||||
pub use get_adresar_count::get_adresar_count;
|
||||
pub use get_adresar_by_position::get_adresar_by_position;
|
||||
27
server/src/adresar/handlers/delete_adresar.rs
Normal file
27
server/src/adresar/handlers/delete_adresar.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
// src/adresar/handlers/delete_adresar.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use common::proto::multieko2::adresar::{DeleteAdresarRequest, DeleteAdresarResponse};
|
||||
|
||||
pub async fn delete_adresar(
|
||||
db_pool: &PgPool,
|
||||
request: DeleteAdresarRequest,
|
||||
) -> Result<DeleteAdresarResponse, Status> {
|
||||
let rows_affected = sqlx::query!(
|
||||
r#"
|
||||
UPDATE adresar
|
||||
SET deleted = true
|
||||
WHERE id = $1
|
||||
"#,
|
||||
request.id
|
||||
)
|
||||
.execute(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?
|
||||
.rows_affected();
|
||||
|
||||
Ok(DeleteAdresarResponse {
|
||||
success: rows_affected > 0,
|
||||
})
|
||||
}
|
||||
|
||||
60
server/src/adresar/handlers/get_adresar.rs
Normal file
60
server/src/adresar/handlers/get_adresar.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
// src/adresar/handlers/get_adresar.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use crate::adresar::models::Adresar;
|
||||
use common::proto::multieko2::adresar::{GetAdresarRequest, AdresarResponse};
|
||||
|
||||
pub async fn get_adresar(
|
||||
db_pool: &PgPool,
|
||||
request: GetAdresarRequest,
|
||||
) -> Result<AdresarResponse, Status> {
|
||||
let adresar = sqlx::query_as!(
|
||||
Adresar,
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
deleted,
|
||||
firma,
|
||||
kz,
|
||||
drc,
|
||||
ulica,
|
||||
psc,
|
||||
mesto,
|
||||
stat,
|
||||
banka,
|
||||
ucet,
|
||||
skladm,
|
||||
ico,
|
||||
kontakt,
|
||||
telefon,
|
||||
skladu,
|
||||
fax
|
||||
FROM adresar
|
||||
WHERE id = $1
|
||||
"#,
|
||||
request.id
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::not_found(e.to_string()))?;
|
||||
|
||||
Ok(AdresarResponse {
|
||||
id: adresar.id,
|
||||
firma: adresar.firma,
|
||||
kz: adresar.kz.unwrap_or_default(),
|
||||
drc: adresar.drc.unwrap_or_default(),
|
||||
ulica: adresar.ulica.unwrap_or_default(),
|
||||
psc: adresar.psc.unwrap_or_default(),
|
||||
mesto: adresar.mesto.unwrap_or_default(),
|
||||
stat: adresar.stat.unwrap_or_default(),
|
||||
banka: adresar.banka.unwrap_or_default(),
|
||||
ucet: adresar.ucet.unwrap_or_default(),
|
||||
skladm: adresar.skladm.unwrap_or_default(),
|
||||
ico: adresar.ico.unwrap_or_default(),
|
||||
kontakt: adresar.kontakt.unwrap_or_default(),
|
||||
telefon: adresar.telefon.unwrap_or_default(),
|
||||
skladu: adresar.skladu.unwrap_or_default(),
|
||||
fax: adresar.fax.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
35
server/src/adresar/handlers/get_adresar_by_position.rs
Normal file
35
server/src/adresar/handlers/get_adresar_by_position.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
// src/adresar/handlers/get_adresar_by_position.rs
|
||||
use tonic::{Status};
|
||||
use sqlx::PgPool;
|
||||
use common::proto::multieko2::adresar::{AdresarResponse, GetAdresarRequest};
|
||||
use common::proto::multieko2::common::PositionRequest;
|
||||
use super::get_adresar;
|
||||
|
||||
pub async fn get_adresar_by_position(
|
||||
db_pool: &PgPool,
|
||||
request: PositionRequest,
|
||||
) -> Result<AdresarResponse, Status> {
|
||||
if request.position < 1 {
|
||||
return Err(Status::invalid_argument("Position must be at least 1"));
|
||||
}
|
||||
|
||||
// Find the ID of the Nth non-deleted record
|
||||
let id: i64 = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT id
|
||||
FROM adresar
|
||||
WHERE deleted = FALSE
|
||||
ORDER BY id ASC
|
||||
OFFSET $1
|
||||
LIMIT 1
|
||||
"#,
|
||||
request.position - 1
|
||||
)
|
||||
.fetch_optional(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?
|
||||
.ok_or_else(|| Status::not_found("Position out of bounds"))?;
|
||||
|
||||
// Now fetch the complete record using the existing get_adresar function
|
||||
get_adresar(db_pool, GetAdresarRequest { id }).await
|
||||
}
|
||||
23
server/src/adresar/handlers/get_adresar_count.rs
Normal file
23
server/src/adresar/handlers/get_adresar_count.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// src/adresar/handlers/get_adresar_count.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use common::proto::multieko2::common::{CountResponse, Empty};
|
||||
|
||||
pub async fn get_adresar_count(
|
||||
db_pool: &PgPool,
|
||||
_request: Empty,
|
||||
) -> Result<CountResponse, Status> {
|
||||
let count: i64 = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT COUNT(*) AS count
|
||||
FROM adresar
|
||||
WHERE deleted = FALSE
|
||||
"#
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(CountResponse { count })
|
||||
}
|
||||
68
server/src/adresar/handlers/post_adresar.rs
Normal file
68
server/src/adresar/handlers/post_adresar.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
// src/adresar/handlers/post_adresar.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use crate::adresar::models::Adresar;
|
||||
use common::proto::multieko2::adresar::{PostAdresarRequest, AdresarResponse};
|
||||
|
||||
pub async fn post_adresar(
|
||||
db_pool: &PgPool,
|
||||
request: PostAdresarRequest,
|
||||
) -> Result<AdresarResponse, Status> {
|
||||
let adresar = sqlx::query_as!(
|
||||
Adresar,
|
||||
r#"
|
||||
INSERT INTO adresar (
|
||||
firma, kz, drc, ulica, psc, mesto, stat, banka, ucet,
|
||||
skladm, ico, kontakt, telefon, skladu, fax, deleted
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9,
|
||||
$10, $11, $12, $13, $14, $15, $16
|
||||
)
|
||||
RETURNING
|
||||
id, deleted, firma, kz, drc, ulica, psc, mesto, stat,
|
||||
banka, ucet, skladm, ico, kontakt, telefon, skladu, fax
|
||||
"#,
|
||||
request.firma,
|
||||
request.kz,
|
||||
request.drc,
|
||||
request.ulica,
|
||||
request.psc,
|
||||
request.mesto,
|
||||
request.stat,
|
||||
request.banka,
|
||||
request.ucet,
|
||||
request.skladm,
|
||||
request.ico,
|
||||
request.kontakt,
|
||||
request.telefon,
|
||||
request.skladu,
|
||||
request.fax,
|
||||
false // Set deleted to false by default
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?;
|
||||
|
||||
Ok(AdresarResponse {
|
||||
id: adresar.id,
|
||||
// Do not include `deleted` in the response since it's not
|
||||
// defined in the proto message.
|
||||
firma: adresar.firma,
|
||||
kz: adresar.kz.unwrap_or_default(),
|
||||
drc: adresar.drc.unwrap_or_default(),
|
||||
ulica: adresar.ulica.unwrap_or_default(),
|
||||
psc: adresar.psc.unwrap_or_default(),
|
||||
mesto: adresar.mesto.unwrap_or_default(),
|
||||
stat: adresar.stat.unwrap_or_default(),
|
||||
banka: adresar.banka.unwrap_or_default(),
|
||||
ucet: adresar.ucet.unwrap_or_default(),
|
||||
skladm: adresar.skladm.unwrap_or_default(),
|
||||
ico: adresar.ico.unwrap_or_default(),
|
||||
kontakt: adresar.kontakt.unwrap_or_default(),
|
||||
telefon: adresar.telefon.unwrap_or_default(),
|
||||
skladu: adresar.skladu.unwrap_or_default(),
|
||||
fax: adresar.fax.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
91
server/src/adresar/handlers/put_adresar.rs
Normal file
91
server/src/adresar/handlers/put_adresar.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
// src/adresar/handlers/put_adresar.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use crate::adresar::models::Adresar;
|
||||
use common::proto::multieko2::adresar::{PutAdresarRequest, AdresarResponse};
|
||||
|
||||
pub async fn put_adresar(
|
||||
db_pool: &PgPool,
|
||||
request: PutAdresarRequest,
|
||||
) -> Result<AdresarResponse, Status> {
|
||||
let adresar = sqlx::query_as!(
|
||||
Adresar,
|
||||
r#"
|
||||
UPDATE adresar
|
||||
SET
|
||||
firma = $2,
|
||||
kz = $3,
|
||||
drc = $4,
|
||||
ulica = $5,
|
||||
psc = $6,
|
||||
mesto = $7,
|
||||
stat = $8,
|
||||
banka = $9,
|
||||
ucet = $10,
|
||||
skladm = $11,
|
||||
ico = $12,
|
||||
kontakt = $13,
|
||||
telefon = $14,
|
||||
skladu = $15,
|
||||
fax = $16
|
||||
WHERE id = $1 AND deleted = FALSE
|
||||
RETURNING
|
||||
id,
|
||||
deleted,
|
||||
firma,
|
||||
kz,
|
||||
drc,
|
||||
ulica,
|
||||
psc,
|
||||
mesto,
|
||||
stat,
|
||||
banka,
|
||||
ucet,
|
||||
skladm,
|
||||
ico,
|
||||
kontakt,
|
||||
telefon,
|
||||
skladu,
|
||||
fax
|
||||
"#,
|
||||
request.id,
|
||||
request.firma,
|
||||
request.kz,
|
||||
request.drc,
|
||||
request.ulica,
|
||||
request.psc,
|
||||
request.mesto,
|
||||
request.stat,
|
||||
request.banka,
|
||||
request.ucet,
|
||||
request.skladm,
|
||||
request.ico,
|
||||
request.kontakt,
|
||||
request.telefon,
|
||||
request.skladu,
|
||||
request.fax
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?;
|
||||
|
||||
Ok(AdresarResponse {
|
||||
id: adresar.id,
|
||||
firma: adresar.firma,
|
||||
kz: adresar.kz.unwrap_or_default(),
|
||||
drc: adresar.drc.unwrap_or_default(),
|
||||
ulica: adresar.ulica.unwrap_or_default(),
|
||||
psc: adresar.psc.unwrap_or_default(),
|
||||
mesto: adresar.mesto.unwrap_or_default(),
|
||||
stat: adresar.stat.unwrap_or_default(),
|
||||
banka: adresar.banka.unwrap_or_default(),
|
||||
ucet: adresar.ucet.unwrap_or_default(),
|
||||
skladm: adresar.skladm.unwrap_or_default(),
|
||||
ico: adresar.ico.unwrap_or_default(),
|
||||
kontakt: adresar.kontakt.unwrap_or_default(),
|
||||
telefon: adresar.telefon.unwrap_or_default(),
|
||||
skladu: adresar.skladu.unwrap_or_default(),
|
||||
fax: adresar.fax.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
4
server/src/adresar/mod.rs
Normal file
4
server/src/adresar/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/adresar/mod.rs
|
||||
|
||||
pub mod models;
|
||||
pub mod handlers;
|
||||
23
server/src/adresar/models.rs
Normal file
23
server/src/adresar/models.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// src/adresar/models.rs
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Adresar {
|
||||
pub id: i64,
|
||||
pub deleted: bool,
|
||||
pub firma: String,
|
||||
pub kz: Option<String>,
|
||||
pub drc: Option<String>,
|
||||
pub ulica: Option<String>,
|
||||
pub psc: Option<String>,
|
||||
pub mesto: Option<String>,
|
||||
pub stat: Option<String>,
|
||||
pub banka: Option<String>,
|
||||
pub ucet: Option<String>,
|
||||
pub skladm: Option<String>,
|
||||
pub ico: Option<String>,
|
||||
pub kontakt: Option<String>,
|
||||
pub telefon: Option<String>,
|
||||
pub skladu: Option<String>,
|
||||
pub fax: Option<String>,
|
||||
}
|
||||
52
server/src/db.rs
Normal file
52
server/src/db.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// src/db.rs
|
||||
use sqlx::postgres::{PgPool, PgPoolOptions};
|
||||
use std::time::Duration;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseConfig {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub database_name: String,
|
||||
pub max_connections: u32,
|
||||
}
|
||||
|
||||
impl DatabaseConfig {
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
username: std::env::var("RUST_DB_USER").expect("RUST_DB_USER must be set"),
|
||||
password: std::env::var("RUST_DB_PASSWORD").expect("RUST_DB_PASSWORD must be set"),
|
||||
host: std::env::var("RUST_DB_HOST").expect("RUST_DB_HOST must be set"),
|
||||
port: std::env::var("RUST_DB_PORT")
|
||||
.expect("RUST_DB_PORT must be set")
|
||||
.parse()
|
||||
.expect("RUST_DB_PORT must be a valid port number"),
|
||||
database_name: std::env::var("RUST_DB_NAME").expect("RUST_DB_NAME must be set"),
|
||||
max_connections: 5,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connection_string(&self) -> String {
|
||||
format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
self.username, self.password, self.host, self.port, self.database_name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_pool(config: &DatabaseConfig) -> Result<PgPool, sqlx::Error> {
|
||||
let conn_str = config.connection_string();
|
||||
info!("Connecting to database: {}", conn_str);
|
||||
PgPoolOptions::new()
|
||||
.max_connections(config.max_connections)
|
||||
.acquire_timeout(Duration::from_secs(3))
|
||||
.connect(&conn_str)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn check_connection(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
sqlx::query("SELECT 1").execute(pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
7
server/src/lib.rs
Normal file
7
server/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// src/lib.rs
|
||||
pub mod db;
|
||||
pub mod server;
|
||||
pub mod adresar;
|
||||
pub mod uctovnictvo;
|
||||
pub mod shared;
|
||||
pub mod table_structure;
|
||||
20
server/src/main.rs
Normal file
20
server/src/main.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
// src/main.rs
|
||||
use std::env;
|
||||
use dotenvy::dotenv;
|
||||
use multieko2::db;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dotenv().ok();
|
||||
|
||||
// Initialize database
|
||||
let db_config = db::DatabaseConfig::from_env();
|
||||
let db_pool = db::create_pool(&db_config).await?;
|
||||
|
||||
match env::args().nth(1).as_deref() {
|
||||
Some("server") => server::run_server(db_pool).await?,
|
||||
_ => println!("Usage: cargo run -- [server|client]"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
4
server/src/server/handlers.rs
Normal file
4
server/src/server/handlers.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/server/handlers.rs
|
||||
pub use crate::server::services::adresar_service::AdresarService;
|
||||
pub use crate::server::services::uctovnictvo_service::UctovnictvoService;
|
||||
pub use crate::server::services::table_structure_service::TableStructureHandler;
|
||||
7
server/src/server/mod.rs
Normal file
7
server/src/server/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// src/server/mod.rs
|
||||
pub mod services;
|
||||
pub mod handlers;
|
||||
pub mod run;
|
||||
|
||||
pub use run::run_server;
|
||||
pub use services::*;
|
||||
31
server/src/server/run.rs
Normal file
31
server/src/server/run.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
// src/server/run.rs
|
||||
use tonic::transport::Server;
|
||||
use tonic_reflection::server::Builder as ReflectionBuilder;
|
||||
|
||||
use common::proto::multieko2::FILE_DESCRIPTOR_SET;
|
||||
use crate::server::services::{
|
||||
AdresarService,
|
||||
UctovnictvoService,
|
||||
TableStructureHandler
|
||||
};
|
||||
use common::proto::multieko2::adresar::adresar_server::AdresarServer;
|
||||
use common::proto::multieko2::uctovnictvo::uctovnictvo_server::UctovnictvoServer;
|
||||
use common::proto::multieko2::table_structure::table_structure_service_server::TableStructureServiceServer;
|
||||
|
||||
pub async fn run_server(db_pool: sqlx::PgPool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "[::1]:50051".parse()?;
|
||||
|
||||
let reflection_service = ReflectionBuilder::configure()
|
||||
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
|
||||
.build_v1()?;
|
||||
|
||||
Server::builder()
|
||||
.add_service(AdresarServer::new(AdresarService { db_pool: db_pool.clone() }))
|
||||
.add_service(UctovnictvoServer::new(UctovnictvoService { db_pool: db_pool.clone() }))
|
||||
.add_service(TableStructureServiceServer::new(TableStructureHandler { db_pool }))
|
||||
.add_service(reflection_service)
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
69
server/src/server/services/adresar_service.rs
Normal file
69
server/src/server/services/adresar_service.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
// src/server/services/adresar_service.rs
|
||||
use tonic::{Request, Response, Status};
|
||||
use common::proto::multieko2::adresar::{
|
||||
adresar_server::Adresar,
|
||||
PostAdresarRequest, AdresarResponse, GetAdresarRequest, PutAdresarRequest,
|
||||
DeleteAdresarRequest, DeleteAdresarResponse,
|
||||
};
|
||||
use common::proto::multieko2::common::{Empty, CountResponse, PositionRequest};
|
||||
use crate::adresar::handlers::{
|
||||
post_adresar, get_adresar, put_adresar, delete_adresar,
|
||||
get_adresar_count, get_adresar_by_position,
|
||||
};
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AdresarService {
|
||||
pub db_pool: PgPool,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Adresar for AdresarService {
|
||||
async fn post_adresar(
|
||||
&self,
|
||||
request: Request<PostAdresarRequest>,
|
||||
) -> Result<Response<AdresarResponse>, Status> {
|
||||
let response = post_adresar(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_adresar(
|
||||
&self,
|
||||
request: Request<GetAdresarRequest>,
|
||||
) -> Result<Response<AdresarResponse>, Status> {
|
||||
let response = get_adresar(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn put_adresar(
|
||||
&self,
|
||||
request: Request<PutAdresarRequest>,
|
||||
) -> Result<Response<AdresarResponse>, Status> {
|
||||
let response = put_adresar(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn delete_adresar(
|
||||
&self,
|
||||
request: Request<DeleteAdresarRequest>,
|
||||
) -> Result<Response<DeleteAdresarResponse>, Status> {
|
||||
let response = delete_adresar(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_adresar_count(
|
||||
&self,
|
||||
request: Request<Empty>,
|
||||
) -> Result<Response<CountResponse>, Status> {
|
||||
let response = get_adresar_count(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_adresar_by_position(
|
||||
&self,
|
||||
request: Request<PositionRequest>,
|
||||
) -> Result<Response<AdresarResponse>, Status> {
|
||||
let response = get_adresar_by_position(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
}
|
||||
10
server/src/server/services/mod.rs
Normal file
10
server/src/server/services/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
// src/server/services/mod.rs
|
||||
|
||||
pub mod adresar_service;
|
||||
pub mod table_structure_service;
|
||||
pub mod uctovnictvo_service;
|
||||
|
||||
pub use adresar_service::AdresarService;
|
||||
pub use table_structure_service::TableStructureHandler;
|
||||
pub use uctovnictvo_service::UctovnictvoService;
|
||||
|
||||
34
server/src/server/services/table_structure_service.rs
Normal file
34
server/src/server/services/table_structure_service.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
// src/server/services/table_structure_service.rs
|
||||
use tonic::{Request, Response, Status};
|
||||
use common::proto::multieko2::table_structure::table_structure_service_server::TableStructureService;
|
||||
use common::proto::multieko2::table_structure::TableStructureResponse;
|
||||
use common::proto::multieko2::common::Empty;
|
||||
use crate::table_structure::handlers::{
|
||||
get_adresar_table_structure, get_uctovnictvo_table_structure,
|
||||
};
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TableStructureHandler {
|
||||
pub db_pool: PgPool,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl TableStructureService for TableStructureHandler {
|
||||
async fn get_adresar_table_structure(
|
||||
&self,
|
||||
request: Request<Empty>,
|
||||
) -> Result<Response<TableStructureResponse>, Status> {
|
||||
let response = get_adresar_table_structure(&self.db_pool, request.into_inner())
|
||||
.await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_uctovnictvo_table_structure(
|
||||
&self,
|
||||
request: Request<Empty>,
|
||||
) -> Result<Response<TableStructureResponse>, Status> {
|
||||
let response = get_uctovnictvo_table_structure(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
}
|
||||
60
server/src/server/services/uctovnictvo_service.rs
Normal file
60
server/src/server/services/uctovnictvo_service.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
// src/server/services/uctovnictvo_service.rs
|
||||
use tonic::{Request, Response, Status};
|
||||
use common::proto::multieko2::uctovnictvo::{
|
||||
uctovnictvo_server::Uctovnictvo,
|
||||
PostUctovnictvoRequest, UctovnictvoResponse, GetUctovnictvoRequest, PutUctovnictvoRequest,
|
||||
};
|
||||
use crate::uctovnictvo::handlers::{
|
||||
post_uctovnictvo, get_uctovnictvo, get_uctovnictvo_count,
|
||||
get_uctovnictvo_by_position, put_uctovnictvo,
|
||||
};
|
||||
use common::proto::multieko2::common::{Empty, CountResponse, PositionRequest};
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UctovnictvoService {
|
||||
pub db_pool: PgPool,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Uctovnictvo for UctovnictvoService {
|
||||
async fn post_uctovnictvo(
|
||||
&self,
|
||||
request: Request<PostUctovnictvoRequest>,
|
||||
) -> Result<Response<UctovnictvoResponse>, Status> {
|
||||
let response = post_uctovnictvo(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_uctovnictvo(
|
||||
&self,
|
||||
request: Request<GetUctovnictvoRequest>,
|
||||
) -> Result<Response<UctovnictvoResponse>, Status> {
|
||||
let response = get_uctovnictvo(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_uctovnictvo_count(
|
||||
&self,
|
||||
request: Request<Empty>,
|
||||
) -> Result<Response<CountResponse>, Status> {
|
||||
let response = get_uctovnictvo_count(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_uctovnictvo_by_position(
|
||||
&self,
|
||||
request: Request<PositionRequest>,
|
||||
) -> Result<Response<UctovnictvoResponse>, Status> {
|
||||
let response = get_uctovnictvo_by_position(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn put_uctovnictvo(
|
||||
&self,
|
||||
request: Request<PutUctovnictvoRequest>,
|
||||
) -> Result<Response<UctovnictvoResponse>, Status> {
|
||||
let response = put_uctovnictvo(&self.db_pool, request.into_inner()).await?;
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
}
|
||||
34
server/src/shared/date_utils.rs
Normal file
34
server/src/shared/date_utils.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
// src/shared/date_utils.rs
|
||||
use chrono::NaiveDate;
|
||||
|
||||
/// Attempts to parse the date string with multiple commonly used formats in Slovakia
|
||||
pub fn parse_date_with_multiple_formats(date_str: &str) -> Option<NaiveDate> {
|
||||
// Define common date formats used in Slovakia
|
||||
let formats = [
|
||||
"%d:%m:%Y", // Original format: 01:01:2023
|
||||
"%d.%m.%Y", // Standard Slovak format: 01.01.2023
|
||||
"%d-%m-%Y", // Dash format: 01-01-2023
|
||||
"%d/%m/%Y", // Slash format: 01/01/2023
|
||||
"%Y-%m-%d", // ISO format: 2023-01-01
|
||||
"%Y/%m/%d", // Alternative ISO: 2023/01/01
|
||||
"%Y.%m.%d", // Dot ISO: 2023.01.01
|
||||
"%d.%m.%y", // Short year with dot: 01.01.23
|
||||
"%d-%m-%y", // Short year with dash: 01-01-23
|
||||
"%d/%m/%y", // Short year with slash: 01/01/23
|
||||
];
|
||||
|
||||
// Try each format until one works
|
||||
for format in formats {
|
||||
if let Ok(date) = NaiveDate::parse_from_str(date_str, format) {
|
||||
return Some(date);
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the formats worked, try to handle potential whitespace issues
|
||||
let trimmed = date_str.trim();
|
||||
if trimmed != date_str {
|
||||
return parse_date_with_multiple_formats(trimmed);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
2
server/src/shared/mod.rs
Normal file
2
server/src/shared/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// src/shared/mod.rs
|
||||
pub mod date_utils;
|
||||
84
server/src/table_structure/docs/response.txt
Normal file
84
server/src/table_structure/docs/response.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
Adresar response:
|
||||
❯ grpcurl -plaintext \
|
||||
-proto proto/table_structure.proto \
|
||||
-import-path proto \
|
||||
localhost:50051 \
|
||||
multieko2.table_structure.TableStructureService/GetAdresarTableStructure
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"name": "firma",
|
||||
"dataType": "TEXT"
|
||||
},
|
||||
{
|
||||
"name": "kz",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "drc",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "ulica",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "psc",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "mesto",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "stat",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "banka",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "ucet",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "skladm",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "ico",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "kontakt",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "telefon",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "skladu",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
},
|
||||
{
|
||||
"name": "fax",
|
||||
"dataType": "TEXT",
|
||||
"isNullable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
4
server/src/table_structure/handlers.rs
Normal file
4
server/src/table_structure/handlers.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/table_structure/handlers.rs
|
||||
pub mod table_structure;
|
||||
|
||||
pub use table_structure::{get_adresar_table_structure, get_uctovnictvo_table_structure};
|
||||
181
server/src/table_structure/handlers/table_structure.rs
Normal file
181
server/src/table_structure/handlers/table_structure.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
// src/table_structure/handlers/table_structure.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use common::proto::multieko2::{
|
||||
table_structure::{TableStructureResponse, TableColumn},
|
||||
common::Empty
|
||||
};
|
||||
|
||||
pub async fn get_adresar_table_structure(
|
||||
_db_pool: &PgPool,
|
||||
_request: Empty,
|
||||
) -> Result<TableStructureResponse, Status> {
|
||||
let columns = vec![
|
||||
TableColumn {
|
||||
name: "firma".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: false,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "kz".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "drc".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "ulica".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "psc".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "mesto".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "stat".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "banka".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "ucet".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "skladm".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "ico".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "kontakt".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "telefon".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "skladu".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "fax".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
];
|
||||
Ok(TableStructureResponse { columns })
|
||||
}
|
||||
|
||||
pub async fn get_uctovnictvo_table_structure(
|
||||
_db_pool: &PgPool,
|
||||
_request: Empty,
|
||||
) -> Result<TableStructureResponse, Status> {
|
||||
let columns = vec![
|
||||
TableColumn {
|
||||
name: "adresar_id".to_string(),
|
||||
data_type: "BIGINT".to_string(),
|
||||
is_nullable: false,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "c_dokladu".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: false,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "datum".to_string(),
|
||||
data_type: "DATE".to_string(),
|
||||
is_nullable: false,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "c_faktury".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: false,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "obsah".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "stredisko".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "c_uctu".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "md".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "identif".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "poznanka".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: true,
|
||||
is_primary_key: false,
|
||||
},
|
||||
TableColumn {
|
||||
name: "firma".to_string(),
|
||||
data_type: "TEXT".to_string(),
|
||||
is_nullable: false,
|
||||
is_primary_key: false,
|
||||
},
|
||||
];
|
||||
Ok(TableStructureResponse { columns })
|
||||
}
|
||||
3
server/src/table_structure/mod.rs
Normal file
3
server/src/table_structure/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
// src/table_structure/mod.rs
|
||||
|
||||
pub mod handlers;
|
||||
58
server/src/uctovnictvo/docs/example.txt
Normal file
58
server/src/uctovnictvo/docs/example.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
POST
|
||||
❯ grpcurl -plaintext -d '{
|
||||
"adresar_id": 1,
|
||||
"c_dokladu": "DOC123",
|
||||
"datum": "01:10:2023",
|
||||
"c_faktury": "INV123",
|
||||
"obsah": "Sample content",
|
||||
"stredisko": "Center A",
|
||||
"c_uctu": "ACC123",
|
||||
"md": "MD123",
|
||||
"identif": "ID123",
|
||||
"poznanka": "Sample note",
|
||||
"firma": "AAA"
|
||||
}' localhost:50051 multieko2.uctovnictvo.Uctovnictvo/PostUctovnictvo
|
||||
{
|
||||
"id": "3",
|
||||
"adresarId": "1",
|
||||
"cDokladu": "DOC123",
|
||||
"datum": "2023-10-01",
|
||||
"cFaktury": "INV123",
|
||||
"obsah": "Sample content",
|
||||
"stredisko": "Center A",
|
||||
"cUctu": "ACC123",
|
||||
"md": "MD123",
|
||||
"identif": "ID123",
|
||||
"poznanka": "Sample note",
|
||||
"firma": "AAA"
|
||||
}
|
||||
|
||||
PUT
|
||||
❯ grpcurl -plaintext -d '{
|
||||
"id": '1',
|
||||
"adresar_id": 1,
|
||||
"c_dokladu": "UPDATED-DOC",
|
||||
"datum": "15.11.2023",
|
||||
"c_faktury": "UPDATED-INV",
|
||||
"obsah": "Updated content",
|
||||
"stredisko": "Updated Center",
|
||||
"c_uctu": "UPD-ACC",
|
||||
"md": "UPD-MD",
|
||||
"identif": "UPD-ID",
|
||||
"poznanka": "Updated note",
|
||||
"firma": "UPD"
|
||||
}' localhost:50051 multieko2.uctovnictvo.Uctovnictvo/PutUctovnictvo
|
||||
{
|
||||
"id": "1",
|
||||
"adresarId": "1",
|
||||
"cDokladu": "UPDATED-DOC",
|
||||
"datum": "15.11.2023",
|
||||
"cFaktury": "UPDATED-INV",
|
||||
"obsah": "Updated content",
|
||||
"stredisko": "Updated Center",
|
||||
"cUctu": "UPD-ACC",
|
||||
"md": "UPD-MD",
|
||||
"identif": "UPD-ID",
|
||||
"poznanka": "Updated note",
|
||||
"firma": "UPD"
|
||||
}
|
||||
41
server/src/uctovnictvo/docs/get_examples.txt
Normal file
41
server/src/uctovnictvo/docs/get_examples.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
❯ grpcurl -plaintext -d '{}' localhost:50051 multieko2.uctovnictvo.Uctovnictvo/GetUctovnictvoCount
|
||||
|
||||
{
|
||||
"count": "4"
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{
|
||||
"position": 2
|
||||
}' localhost:50051 multieko2.uctovnictvo.Uctovnictvo/GetUctovnictvoByPosition
|
||||
|
||||
{
|
||||
"id": "2",
|
||||
"adresarId": "1",
|
||||
"cDokladu": "DOC123",
|
||||
"datum": "01.10.2023",
|
||||
"cFaktury": "INV123",
|
||||
"obsah": "Sample content",
|
||||
"stredisko": "Center A",
|
||||
"cUctu": "ACC123",
|
||||
"md": "MD123",
|
||||
"identif": "ID123",
|
||||
"poznanka": "Sample note",
|
||||
"firma": "AAA"
|
||||
}
|
||||
❯ grpcurl -plaintext -d '{
|
||||
"id": 1
|
||||
}' localhost:50051 multieko2.uctovnictvo.Uctovnictvo/GetUctovnictvo
|
||||
{
|
||||
"id": "1",
|
||||
"adresarId": "1",
|
||||
"cDokladu": "DOC123",
|
||||
"datum": "01.10.2023",
|
||||
"cFaktury": "INV123",
|
||||
"obsah": "Sample content",
|
||||
"stredisko": "Center A",
|
||||
"cUctu": "ACC123",
|
||||
"md": "MD123",
|
||||
"identif": "ID123",
|
||||
"poznanka": "Sample note",
|
||||
"firma": "AAA"
|
||||
}
|
||||
|
||||
12
server/src/uctovnictvo/handlers.rs
Normal file
12
server/src/uctovnictvo/handlers.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
// src/uctovnictvo/handlers.rs
|
||||
pub mod post_uctovnictvo;
|
||||
pub mod get_uctovnictvo;
|
||||
pub mod get_uctovnictvo_count;
|
||||
pub mod get_uctovnictvo_by_position;
|
||||
pub mod put_uctovnictvo;
|
||||
|
||||
pub use post_uctovnictvo::post_uctovnictvo;
|
||||
pub use get_uctovnictvo::get_uctovnictvo;
|
||||
pub use get_uctovnictvo_count::get_uctovnictvo_count;
|
||||
pub use get_uctovnictvo_by_position::get_uctovnictvo_by_position;
|
||||
pub use put_uctovnictvo::put_uctovnictvo;
|
||||
51
server/src/uctovnictvo/handlers/get_uctovnictvo.rs
Normal file
51
server/src/uctovnictvo/handlers/get_uctovnictvo.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
// src/uctovnictvo/handlers/get_uctovnictvo.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use crate::uctovnictvo::models::Uctovnictvo;
|
||||
use common::proto::multieko2::uctovnictvo::{GetUctovnictvoRequest, UctovnictvoResponse};
|
||||
|
||||
pub async fn get_uctovnictvo(
|
||||
db_pool: &PgPool,
|
||||
request: GetUctovnictvoRequest,
|
||||
) -> Result<UctovnictvoResponse, Status> {
|
||||
let uctovnictvo = sqlx::query_as!(
|
||||
Uctovnictvo,
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
deleted,
|
||||
adresar_id,
|
||||
c_dokladu,
|
||||
datum as "datum: chrono::NaiveDate",
|
||||
c_faktury,
|
||||
obsah,
|
||||
stredisko,
|
||||
c_uctu,
|
||||
md,
|
||||
identif,
|
||||
poznanka,
|
||||
firma
|
||||
FROM uctovnictvo
|
||||
WHERE id = $1
|
||||
"#,
|
||||
request.id
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::not_found(e.to_string()))?;
|
||||
|
||||
Ok(UctovnictvoResponse {
|
||||
id: uctovnictvo.id,
|
||||
adresar_id: uctovnictvo.adresar_id,
|
||||
c_dokladu: uctovnictvo.c_dokladu,
|
||||
datum: uctovnictvo.datum.format("%d.%m.%Y").to_string(),
|
||||
c_faktury: uctovnictvo.c_faktury,
|
||||
obsah: uctovnictvo.obsah.unwrap_or_default(),
|
||||
stredisko: uctovnictvo.stredisko.unwrap_or_default(),
|
||||
c_uctu: uctovnictvo.c_uctu.unwrap_or_default(),
|
||||
md: uctovnictvo.md.unwrap_or_default(),
|
||||
identif: uctovnictvo.identif.unwrap_or_default(),
|
||||
poznanka: uctovnictvo.poznanka.unwrap_or_default(),
|
||||
firma: uctovnictvo.firma,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// src/uctovnictvo/handlers/get_uctovnictvo_by_position.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use common::proto::multieko2::common::PositionRequest;
|
||||
use super::get_uctovnictvo;
|
||||
|
||||
pub async fn get_uctovnictvo_by_position(
|
||||
db_pool: &PgPool,
|
||||
request: PositionRequest,
|
||||
) -> Result<common::proto::multieko2::uctovnictvo::UctovnictvoResponse, Status> {
|
||||
if request.position < 1 {
|
||||
return Err(Status::invalid_argument("Position must be at least 1"));
|
||||
}
|
||||
|
||||
// Find the ID of the Nth non-deleted record
|
||||
let id: i64 = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT id
|
||||
FROM uctovnictvo
|
||||
WHERE deleted = FALSE
|
||||
ORDER BY id ASC
|
||||
OFFSET $1
|
||||
LIMIT 1
|
||||
"#,
|
||||
request.position - 1
|
||||
)
|
||||
.fetch_optional(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?
|
||||
.ok_or_else(|| Status::not_found("Position out of bounds"))?;
|
||||
|
||||
// Now fetch the complete record using the existing get_uctovnictvo function
|
||||
get_uctovnictvo(db_pool, common::proto::multieko2::uctovnictvo::GetUctovnictvoRequest { id }).await
|
||||
}
|
||||
23
server/src/uctovnictvo/handlers/get_uctovnictvo_count.rs
Normal file
23
server/src/uctovnictvo/handlers/get_uctovnictvo_count.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// src/uctovnictvo/handlers/get_uctovnictvo_count.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use common::proto::multieko2::common::{CountResponse, Empty};
|
||||
|
||||
pub async fn get_uctovnictvo_count(
|
||||
db_pool: &PgPool,
|
||||
_request: Empty,
|
||||
) -> Result<CountResponse, Status> {
|
||||
let count: i64 = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT COUNT(*) AS count
|
||||
FROM uctovnictvo
|
||||
WHERE deleted = FALSE
|
||||
"#
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(CountResponse { count })
|
||||
}
|
||||
73
server/src/uctovnictvo/handlers/post_uctovnictvo.rs
Normal file
73
server/src/uctovnictvo/handlers/post_uctovnictvo.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// src/uctovnictvo/handlers/post_uctovnictvo.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use crate::uctovnictvo::models::Uctovnictvo;
|
||||
use common::proto::multieko2::uctovnictvo::{PostUctovnictvoRequest, UctovnictvoResponse};
|
||||
use crate::shared::date_utils::parse_date_with_multiple_formats; // Import from shared module
|
||||
|
||||
pub async fn post_uctovnictvo(
|
||||
db_pool: &PgPool,
|
||||
request: PostUctovnictvoRequest,
|
||||
) -> Result<UctovnictvoResponse, Status> {
|
||||
let datum = parse_date_with_multiple_formats(&request.datum)
|
||||
.ok_or_else(|| Status::invalid_argument(format!("Invalid date format: {}", request.datum)))?;
|
||||
|
||||
// Pass the NaiveDate value directly.
|
||||
let uctovnictvo = sqlx::query_as!(
|
||||
Uctovnictvo,
|
||||
r#"
|
||||
INSERT INTO uctovnictvo (
|
||||
adresar_id, c_dokladu, datum, c_faktury, obsah, stredisko,
|
||||
c_uctu, md, identif, poznanka, firma, deleted
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
|
||||
)
|
||||
RETURNING
|
||||
id,
|
||||
deleted,
|
||||
adresar_id,
|
||||
c_dokladu,
|
||||
datum as "datum: chrono::NaiveDate",
|
||||
c_faktury,
|
||||
obsah,
|
||||
stredisko,
|
||||
c_uctu,
|
||||
md,
|
||||
identif,
|
||||
poznanka,
|
||||
firma
|
||||
"#,
|
||||
request.adresar_id,
|
||||
request.c_dokladu,
|
||||
datum as chrono::NaiveDate,
|
||||
request.c_faktury,
|
||||
request.obsah,
|
||||
request.stredisko,
|
||||
request.c_uctu,
|
||||
request.md,
|
||||
request.identif,
|
||||
request.poznanka,
|
||||
request.firma,
|
||||
false
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?;
|
||||
|
||||
// Return the response with formatted date
|
||||
Ok(UctovnictvoResponse {
|
||||
id: uctovnictvo.id,
|
||||
adresar_id: uctovnictvo.adresar_id,
|
||||
c_dokladu: uctovnictvo.c_dokladu,
|
||||
datum: uctovnictvo.datum.format("%d.%m.%Y").to_string(), // Standard Slovak format
|
||||
c_faktury: uctovnictvo.c_faktury,
|
||||
obsah: uctovnictvo.obsah.unwrap_or_default(),
|
||||
stredisko: uctovnictvo.stredisko.unwrap_or_default(),
|
||||
c_uctu: uctovnictvo.c_uctu.unwrap_or_default(),
|
||||
md: uctovnictvo.md.unwrap_or_default(),
|
||||
identif: uctovnictvo.identif.unwrap_or_default(),
|
||||
poznanka: uctovnictvo.poznanka.unwrap_or_default(),
|
||||
firma: uctovnictvo.firma,
|
||||
})
|
||||
}
|
||||
78
server/src/uctovnictvo/handlers/put_uctovnictvo.rs
Normal file
78
server/src/uctovnictvo/handlers/put_uctovnictvo.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
// src/uctovnictvo/handlers/put_uctovnictvo.rs
|
||||
use tonic::Status;
|
||||
use sqlx::PgPool;
|
||||
use crate::uctovnictvo::models::Uctovnictvo;
|
||||
use common::proto::multieko2::uctovnictvo::{PutUctovnictvoRequest, UctovnictvoResponse};
|
||||
use crate::shared::date_utils::parse_date_with_multiple_formats; // Import from shared module
|
||||
|
||||
pub async fn put_uctovnictvo(
|
||||
db_pool: &PgPool,
|
||||
request: PutUctovnictvoRequest,
|
||||
) -> Result<UctovnictvoResponse, Status> {
|
||||
let datum = parse_date_with_multiple_formats(&request.datum)
|
||||
.ok_or_else(|| Status::invalid_argument("Invalid date format"))?;
|
||||
|
||||
let uctovnictvo = sqlx::query_as!(
|
||||
Uctovnictvo,
|
||||
r#"
|
||||
UPDATE uctovnictvo
|
||||
SET
|
||||
adresar_id = $2,
|
||||
c_dokladu = $3,
|
||||
datum = $4,
|
||||
c_faktury = $5,
|
||||
obsah = $6,
|
||||
stredisko = $7,
|
||||
c_uctu = $8,
|
||||
md = $9,
|
||||
identif = $10,
|
||||
poznanka = $11,
|
||||
firma = $12
|
||||
WHERE id = $1 AND deleted = FALSE
|
||||
RETURNING
|
||||
id,
|
||||
deleted,
|
||||
adresar_id,
|
||||
c_dokladu,
|
||||
datum as "datum: chrono::NaiveDate",
|
||||
c_faktury,
|
||||
obsah,
|
||||
stredisko,
|
||||
c_uctu,
|
||||
md,
|
||||
identif,
|
||||
poznanka,
|
||||
firma
|
||||
"#,
|
||||
request.id,
|
||||
request.adresar_id,
|
||||
request.c_dokladu,
|
||||
datum as chrono::NaiveDate,
|
||||
request.c_faktury,
|
||||
request.obsah,
|
||||
request.stredisko,
|
||||
request.c_uctu,
|
||||
request.md,
|
||||
request.identif,
|
||||
request.poznanka,
|
||||
request.firma
|
||||
)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(e.to_string()))?;
|
||||
|
||||
Ok(UctovnictvoResponse {
|
||||
id: uctovnictvo.id,
|
||||
adresar_id: uctovnictvo.adresar_id,
|
||||
c_dokladu: uctovnictvo.c_dokladu,
|
||||
datum: uctovnictvo.datum.format("%d.%m.%Y").to_string(),
|
||||
c_faktury: uctovnictvo.c_faktury,
|
||||
obsah: uctovnictvo.obsah.unwrap_or_default(),
|
||||
stredisko: uctovnictvo.stredisko.unwrap_or_default(),
|
||||
c_uctu: uctovnictvo.c_uctu.unwrap_or_default(),
|
||||
md: uctovnictvo.md.unwrap_or_default(),
|
||||
identif: uctovnictvo.identif.unwrap_or_default(),
|
||||
poznanka: uctovnictvo.poznanka.unwrap_or_default(),
|
||||
firma: uctovnictvo.firma,
|
||||
})
|
||||
}
|
||||
4
server/src/uctovnictvo/mod.rs
Normal file
4
server/src/uctovnictvo/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/uctovnictvo/mod.rs
|
||||
|
||||
pub mod models;
|
||||
pub mod handlers;
|
||||
21
server/src/uctovnictvo/models.rs
Normal file
21
server/src/uctovnictvo/models.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// src/uctovnictvo/models.rs
|
||||
use chrono::NaiveDate;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, sqlx::FromRow, Serialize, Deserialize)]
|
||||
pub struct Uctovnictvo {
|
||||
pub id: i64,
|
||||
pub deleted: bool,
|
||||
pub adresar_id: i64,
|
||||
pub c_dokladu: String,
|
||||
pub datum: NaiveDate,
|
||||
pub c_faktury: String,
|
||||
pub obsah: Option<String>,
|
||||
pub stredisko: Option<String>,
|
||||
pub c_uctu: Option<String>,
|
||||
pub md: Option<String>,
|
||||
pub identif: Option<String>,
|
||||
pub poznanka: Option<String>,
|
||||
pub firma: String,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user