implement api/cart

This commit is contained in:
Mikkel Troels Kongsted 2025-03-13 14:48:52 +01:00
parent 70b8244e62
commit b486bb7970
9 changed files with 133 additions and 12 deletions

View File

@ -42,9 +42,12 @@ CREATE TABLE IF NOT EXISTS carts (
CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY,
cart INTEGER NOT NULL,
product INTEGER NOT NULL,
amount INTEGER NOT NULL,
FOREIGN KEY(cart) REFERENCES carts(id)
FOREIGN KEY(cart) REFERENCES carts(id),
FOREIGN KEY(product) REFERENCES product(id)
);

View File

@ -32,6 +32,8 @@ void route_get_not_found(HttpCtx* ctx);
void route_get_products_all(HttpCtx* ctx);
void route_get_cart_items_from_session(HttpCtx* ctx);
void route_post_users_register(HttpCtx* ctx);
void route_post_sessions_login(HttpCtx* ctx);

View File

@ -0,0 +1,42 @@
#include "../controllers.h"
#include "../http_server.h"
#include "../models_json.h"
#include "../str_util.h"
void route_get_cart_items_from_session(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const Session* session = middleware_session(ctx);
if (!session)
return;
CartItemVec cart_items;
cart_item_vec_construct(&cart_items);
DbRes db_res = db_cart_items_with_user_id(cx->db, &cart_items, session->user_id);
if (db_res != DbRes_Ok) {
RESPOND_BAD_REQUEST(ctx, "Could not find cart for user");
return;
}
String res;
string_construct(&res);
string_push_str(&res, "{\"ok\":true,\"products\":[");
for (size_t i = 0; i < cart_items.size; ++i) {
if (i != 0) {
string_push_str(&res, ",");
}
char* json = cart_item_to_json_string(&cart_items.data[i]);
string_push_str(&res, json);
free(json);
}
string_push_str(&res, "]}");
cart_item_vec_destroy(&cart_items);
RESPOND_JSON(ctx, 200, "%s", res.data);
string_destroy(&res);
}

View File

@ -27,7 +27,7 @@ void route_post_sessions_login(HttpCtx* ctx)
}
User user;
DbRes db_res = db_user_from_email(cx->db, &user, req.email);
DbRes db_res = db_user_with_email(cx->db, &user, req.email);
if (db_res == DbRes_NotFound) {
RESPOND_BAD_REQUEST(ctx, "user with email not found");
goto l0_return;
@ -71,7 +71,7 @@ void route_get_sessions_user(HttpCtx* ctx)
return;
User user;
DbRes db_res = db_user_from_id(cx->db, &user, session->user_id);
DbRes db_res = db_user_with_id(cx->db, &user, session->user_id);
if (db_res != DbRes_Ok) {
RESPOND_BAD_REQUEST(ctx, "user not found");
return;

View File

@ -2,9 +2,12 @@
#include "collection.h"
#include "models.h"
#include <stdint.h>
DEFINE_VEC(int64_t, Ids, ids, 8)
DEFINE_VEC(Product, ProductVec, product_vec, 32)
DEFINE_VEC(CartItem, CartItemVec, cart_item_vec, 32)
typedef enum {
DbRes_Ok,
@ -17,7 +20,7 @@ typedef struct Db Db;
/// `user.id` field is ignored.
DbRes db_user_insert(Db* db, const User* user);
/// `user` field is an out parameter.
DbRes db_user_from_id(Db* db, User* user, int64_t id);
DbRes db_user_with_id(Db* db, User* user, int64_t id);
/// Expects `ids` to be constructed.
@ -25,7 +28,10 @@ DbRes db_users_with_email(Db* db, Ids* ids, const char* email);
/// `user` is an out parameter.
DbRes db_user_from_email(Db* db, User* user, const char* email);
DbRes db_user_with_email(Db* db, User* user, const char* email);
/// Expects `vec` to be constructed.
DbRes db_product_all(Db* db, ProductVec* vec);
DbRes db_cart_items_with_user_id(Db* db, CartItemVec* vec, int64_t user_id);

View File

@ -4,6 +4,7 @@
#include "str_util.h"
#include <assert.h>
#include <sqlite3.h>
#include <stdint.h>
#include <stdio.h>
static inline char* get_str_safe(sqlite3_stmt* stmt, int col)
@ -100,7 +101,7 @@ l0_return:
return res;
}
DbRes db_user_from_id(Db* db, User* user, int64_t id)
DbRes db_user_with_id(Db* db, User* user, int64_t id)
{
static_assert(sizeof(User) == 40, "model has changed");
@ -118,7 +119,6 @@ DbRes db_user_from_id(Db* db, User* user, int64_t id)
int step_res = sqlite3_step(stmt);
if (step_res == SQLITE_DONE) {
res = DbRes_NotFound;
puts("didn't find user");
goto l0_return;
} else if (step_res != SQLITE_ROW) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
@ -173,7 +173,7 @@ l0_return:
return res;
}
DbRes db_user_from_email(Db* db, User* user, const char* email)
DbRes db_user_with_email(Db* db, User* user, const char* email)
{
static_assert(sizeof(User) == 40, "model has changed");
@ -263,3 +263,65 @@ l0_return:
DISCONNECT;
return res;
}
DbRes db_cart_items_with_user_id(Db* db, CartItemVec* vec, int64_t user_id) {
static_assert(sizeof(Cart) == 16, "model has changed");
static_assert(sizeof(CartItem) == 32, "model has changed");
sqlite3* connection;
CONNECT;
DbRes res;
sqlite3_stmt* stmt;
int sqlite_res = sqlite3_prepare_v2(connection,
"SELECT id "
" FROM carts WHERE user = ?",
-1, &stmt, NULL);
if (sqlite_res != SQLITE_OK) {
fprintf(stderr, "error: %s\n at %s:%d\n", sqlite3_errmsg(connection),
__func__, __LINE__);
res = DbRes_Error;
goto l0_return;
}
sqlite3_bind_int64(stmt, 1, user_id);
int step_res = sqlite3_step(stmt);
if (step_res == SQLITE_DONE) {
res = DbRes_NotFound;
goto l0_return;
} else if (step_res != SQLITE_ROW) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
res = DbRes_Error;
goto l0_return;
}
int64_t cart_id = GET_INT(0);
sqlite_res = sqlite3_prepare_v2(
connection, "SELECT id, cart, product, amount FROM cart_items WHERE cart = ?", -1, &stmt, NULL);
sqlite3_bind_int64(stmt, 1, cart_id);
while ((sqlite_res = sqlite3_step(stmt)) == SQLITE_ROW) {
CartItem cart_item = {
.id = GET_INT(0),
.cart_id = GET_INT(1),
.product_id = GET_INT(2),
.amount = GET_INT(3),
};
cart_item_vec_push(vec, cart_item);
}
if (sqlite_res != SQLITE_DONE) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
res = DbRes_Error;
goto l0_return;
}
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}

View File

@ -43,6 +43,8 @@ int main(void)
http_server_get(server, "/api/products/all", route_get_products_all);
http_server_get(server, "/api/cart", route_get_cart_items_from_session);
http_server_post(server, "/api/users/register", route_post_users_register);
http_server_post(server, "/api/sessions/login", route_post_sessions_login);
http_server_post(

View File

@ -47,7 +47,7 @@ void cart_destroy(Cart* m)
void cart_item_destroy(CartItem* m)
{
static_assert(sizeof(CartItem) == 24, "model has changed");
static_assert(sizeof(CartItem) == 32, "model has changed");
(void)m;
}
@ -170,7 +170,7 @@ char* cart_to_json_string(const Cart* m)
char* cart_item_to_json_string(const CartItem* m)
{
static_assert(sizeof(CartItem) == 24, "model has changed");
static_assert(sizeof(CartItem) == 32, "model has changed");
String string;
string_construct(&string);
@ -178,9 +178,10 @@ char* cart_item_to_json_string(const CartItem* m)
"{"
"\"id\":%ld,"
"\"cart_id\":%ld,"
"\"product_id\":%ld,"
"\"amount\":%ld"
"}",
m->id, m->cart_id, m->amount);
m->id, m->cart_id, m->product_id, m->amount);
char* result = string_copy(&string);
string_destroy(&string);
@ -354,11 +355,12 @@ int cart_from_json(Cart* m, const JsonValue* json)
int cart_item_from_json(CartItem* m, const JsonValue* json)
{
static_assert(sizeof(CartItem) == 24, "model has changed");
static_assert(sizeof(CartItem) == 32, "model has changed");
ObjField fields[] = {
{ "id", JsonType_Number },
{ "cart_id", JsonType_Number },
{ "product_id", JsonType_Number },
{ "amount", JsonType_Number },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
@ -366,6 +368,7 @@ int cart_item_from_json(CartItem* m, const JsonValue* json)
*m = (CartItem) {
.id = GET_INT("id"),
.cart_id = GET_INT("cart_id"),
.product_id = GET_INT("product_id"),
.amount = GET_INT("amount"),
};
return 0;

View File

@ -39,6 +39,7 @@ typedef struct {
typedef struct {
int64_t id;
int64_t cart_id;
int64_t product_id;
int64_t amount;
} CartItem;