add coords

This commit is contained in:
SimonFJ20 2025-03-21 11:43:13 +01:00
parent cfc73f0868
commit fb26face86
9 changed files with 327 additions and 3 deletions

View File

@ -38,6 +38,8 @@ void route_get_not_found(HttpCtx* ctx);
void route_get_products_all(HttpCtx* ctx);
void route_post_products_create(HttpCtx* ctx);
void route_post_products_update(HttpCtx* ctx);
void route_get_products_coords(HttpCtx* ctx);
void route_post_products_set_coords(HttpCtx* ctx);
void route_post_products_set_image(HttpCtx* ctx);
void route_get_products_image_png(HttpCtx* ctx);

View File

@ -2,6 +2,7 @@
#include "../models/models_json.h"
#include "../utils/str.h"
#include "controllers.h"
#include <stdint.h>
#include <stdio.h>
void route_get_products_all(HttpCtx* ctx)
@ -82,10 +83,8 @@ void route_post_products_update(HttpCtx* ctx)
Cx* cx = http_ctx_user_ctx(ctx);
const char* body_str = http_ctx_req_body_str(ctx);
printf("body_str = '%s'\n", body_str);
JsonValue* body_json = json_parse(body_str, strlen(body_str));
printf("body_json = %p\n", (void*)body_json);
if (!body_json) {
RESPOND_BAD_REQUEST(ctx, "bad request");
@ -94,7 +93,6 @@ void route_post_products_update(HttpCtx* ctx)
Product product;
int parse_result = product_from_json(&product, body_json);
printf("parse_result = %d\n", parse_result);
json_free(body_json);
if (parse_result != 0) {
RESPOND_BAD_REQUEST(ctx, "bad request");
@ -113,6 +111,102 @@ l0_return:
product_destroy(&product);
}
void route_get_products_coords(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const char* query = http_ctx_req_query(ctx);
if (!query) {
RESPOND_BAD_REQUEST(ctx, "no product_id parameter");
return;
}
HttpQueryParams* params = http_parse_query_params(query);
char* product_id_str = http_query_params_get(params, "product_id");
http_query_params_free(params);
if (!product_id_str) {
RESPOND_BAD_REQUEST(ctx, "no product_id parameter");
return;
}
int64_t product_id = strtol(product_id_str, NULL, 10);
free(product_id_str);
Coord coord;
DbRes db_res = db_coord_with_product_id(cx->db, &coord, product_id);
if (db_res == DbRes_NotFound) {
RESPOND_JSON(ctx, 200, "{\"ok\":true,\"found\":false}");
return;
} else if (db_res != DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
return;
}
char* coord_json = coord_to_json_string(&coord);
RESPOND_JSON(
ctx, 200, "{\"ok\":true,\"found\":true,\"coords\":%s}", coord_json);
free(coord_json);
coord_destroy(&coord);
}
void route_post_products_set_coords(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const char* body_str = http_ctx_req_body_str(ctx);
JsonValue* body_json = json_parse(body_str, strlen(body_str));
if (!body_json) {
RESPOND_BAD_REQUEST(ctx, "bad request");
return;
}
ProductsCoordsSetReq req;
int parse_result = products_coords_set_req_from_json(&req, body_json);
json_free(body_json);
if (parse_result != 0) {
RESPOND_BAD_REQUEST(ctx, "bad request");
return;
}
Product product;
DbRes db_res = db_product_with_id(cx->db, &product, req.product_id);
if (db_res == DbRes_NotFound) {
RESPOND_BAD_REQUEST(ctx, "not found");
goto l0_return;
} else if (db_res != DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
goto l0_return;
}
Coord coord = {
.id = 0,
.x = req.x,
.y = req.y,
};
int64_t coord_id;
db_res = db_coord_insert(cx->db, &coord, &coord_id);
if (db_res != DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
goto l1_return;
}
product.coord_id = coord_id;
db_res = db_product_update(cx->db, &product);
if (db_res != DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
goto l1_return;
}
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
l1_return:
coord_destroy(&coord);
products_coords_set_req_destroy(&req);
l0_return:
product_destroy(&product);
}
void route_post_products_set_image(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);

View File

@ -25,6 +25,7 @@ DbRes db_user_update(Db* db, const User* user);
/// `user` field is an out parameter.
DbRes db_user_with_id(Db* db, User* user, int64_t id);
/// `exists` field is an out parameter.
DbRes db_user_with_email_exists(Db* db, bool* exists, const char* email);
/// `user` is an out parameter.
@ -42,6 +43,12 @@ DbRes db_product_with_id(Db* db, Product* product, int64_t id);
/// Expects `vec` to be constructed.
DbRes db_product_all(Db* db, ProductVec* vec);
/// `coord.id` are ignored.
DbRes db_coord_insert(Db* db, const Coord* coord, int64_t* id);
/// `coord` is an out parameter.
DbRes db_coord_with_product_id(Db* db, Coord* coord, int64_t product_id);
/// `product_price` is an out parameter.
DbRes db_product_price_of_product(
Db* db, ProductPrice* product_price, int64_t product_id);

View File

@ -472,6 +472,100 @@ l0_return:
return res;
}
/// `coord.id` are ignored.
DbRes db_coord_insert(Db* db, const Coord* coord, int64_t* id)
{
static_assert(sizeof(Coord) == 24, "model has changed");
sqlite3* connection;
CONNECT;
DbRes res;
sqlite3_stmt* stmt;
int prepare_res = sqlite3_prepare_v2(connection,
"INSERT INTO coords"
" (x, y)"
" VALUES (?, ?)",
-1,
&stmt,
NULL);
if (prepare_res != SQLITE_OK) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
sqlite3_bind_int64(stmt, 1, coord->x);
sqlite3_bind_int64(stmt, 2, coord->y);
int step_res = sqlite3_step(stmt);
if (step_res != SQLITE_DONE) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
int64_t coord_id = sqlite3_last_insert_rowid(connection);
if (id) {
*id = coord_id;
}
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}
DbRes db_coord_with_product_id(Db* db, Coord* coord, int64_t product_id)
{
static_assert(sizeof(Coord) == 24, "model has changed");
sqlite3* connection;
CONNECT;
DbRes res;
sqlite3_stmt* stmt;
int prepare_res = sqlite3_prepare_v2(connection,
"SELECT coords.id, coords.x, coords.y"
" FROM coords"
" JOIN products ON products.coord = coords.id"
" WHERE products.id = ?",
-1,
&stmt,
NULL);
if (prepare_res != SQLITE_OK) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
sqlite3_bind_int64(stmt, 1, product_id);
int step_res = sqlite3_step(stmt);
if (step_res == SQLITE_DONE) {
res = DbRes_NotFound;
goto l0_return;
} else if (step_res != SQLITE_ROW) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
*coord = (Coord){
.id = GET_INT(0),
.x = GET_INT(1),
.y = GET_INT(2),
},
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}
static inline DbRes get_product_price_from_product_id(
sqlite3* connection, int64_t product_id, int64_t* price)
{

View File

@ -40,6 +40,9 @@ int main(void)
server, "/api/products/create", route_post_products_create);
http_server_post(
server, "/api/products/update", route_post_products_update);
http_server_get(server, "/api/products/coords", route_get_products_coords);
http_server_post(
server, "/api/products/set_coords", route_post_products_set_coords);
http_server_post(
server, "/api/products/set-image", route_post_products_set_image);
http_server_get(

View File

@ -113,6 +113,13 @@ void products_create_req_destroy(ProductsCreateReq* m)
free(m->barcode);
}
void products_coords_set_req_destroy(ProductsCoordsSetReq* model)
{
static_assert(sizeof(ProductsCoordsSetReq) == 24, "model has changed");
(void)model;
}
char* user_to_json_string(const User* m)
{
static_assert(sizeof(User) == 40, "model has changed");
@ -397,6 +404,27 @@ char* products_create_req_to_json_string(const ProductsCreateReq* m)
return result;
}
char* products_coords_set_req_to_json_string(const ProductsCoordsSetReq* m)
{
static_assert(sizeof(ProductsCoordsSetReq) == 24, "model has changed");
String string;
string_construct(&string);
string_pushf(&string,
"{"
"\"product_id\":%ld,"
"\"x\":%ld,"
"\"y\":%ld"
"}",
m->product_id,
m->x,
m->y);
char* result = string_copy(&string);
string_destroy(&string);
return result;
}
typedef struct {
const char* key;
JsonType type;
@ -639,6 +667,25 @@ int products_create_req_from_json(ProductsCreateReq* m, const JsonValue* json)
};
return 0;
}
int products_coords_set_req_from_json(
ProductsCoordsSetReq* m, const JsonValue* json)
{
static_assert(sizeof(ProductsCoordsSetReq) == 24, "model has changed");
ObjField fields[] = {
{ "product_id", JsonType_Number },
{ "x", JsonType_Number },
{ "y", JsonType_Number },
};
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (ProductsCoordsSetReq) {
.product_id = GET_INT("product_id"),
.x = GET_INT("x"),
.y = GET_INT("y"),
};
return 0;
}
DEFINE_VEC_IMPL(ProductPrice, ProductPriceVec, product_price_vec, )
DEFINE_VEC_IMPL(ReceiptProduct, ReceiptProductVec, receipt_product_vec, )

View File

@ -131,3 +131,11 @@ typedef struct {
} ProductsCreateReq;
void products_create_req_destroy(ProductsCreateReq* model);
typedef struct {
int64_t product_id;
int64_t x;
int64_t y;
} ProductsCoordsSetReq;
void products_coords_set_req_destroy(ProductsCoordsSetReq* model);

View File

@ -18,3 +18,4 @@ DEFINE_MODEL_JSON(CartsPurchaseReq, carts_purchase_req)
DEFINE_MODEL_JSON(ReceiptsOneResProduct, receipts_one_res_product)
DEFINE_MODEL_JSON(ReceiptsOneRes, receipts_one_res)
DEFINE_MODEL_JSON(ProductsCreateReq, products_create_req)
DEFINE_MODEL_JSON(ProductsCoordsSetReq, products_coords_set_req)

View File

@ -67,6 +67,8 @@ Deno.test("test backend", async (t) => {
await testCartsAndReceipts(t, token!);
await testProductsCoords(t);
await t.step("test /api/sessions/logout", async () => {
const logoutRes = await post<{ ok: boolean }>(
"/api/sessions/logout",
@ -154,6 +156,72 @@ async function testCartsAndReceipts(t: Deno.TestContext, token: string) {
});
}
async function testProductsCoords(t: Deno.TestContext) {
const productId = 1;
let coords: { x: number; y: number } | null = null;
await t.step("test /api/products/coords", async () => {
const res = await get<{
ok: boolean;
found: boolean;
coords: { x: number; y: number };
}>(
`/api/products/coords?product_id=${productId}`,
{},
);
// console.log(res);
assertEquals(res.ok, true);
if (res.found) {
coords = res.coords;
} else {
coords = null;
}
});
await t.step("test /api/products/set_coords", async () => {
const res = await post<{ ok: boolean }>(
"/api/products/set_coords",
{
product_id: productId,
x: 1,
y: 2,
},
{},
);
// console.log(res);
assertEquals(res.ok, true);
});
await t.step("test /api/products/coords", async () => {
const res = await get<{
ok: boolean;
found: true;
coords: { x: number; y: number };
}>(
`/api/products/coords?product_id=${productId}`,
{},
);
// console.log(res);
assertEquals(res.ok, true);
assertEquals(res.found, true);
assertEquals(res.coords.x, 1);
assertEquals(res.coords.y, 2);
});
if (coords !== null) {
const res = await post<{ ok: boolean }>(
"/api/products/set_coords",
{ product_id: productId, ...coords as { x: number; y: number } },
{},
);
// console.log(res);
assertEquals(res.ok, true);
}
}
type SessionUser = { id: number; email: string; balance_dkk_cent: number };
async function sessionUser(