mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
carts purchase first half
This commit is contained in:
parent
b3d911685e
commit
44ef95ba63
@ -32,19 +32,6 @@ CREATE TABLE IF NOT EXISTS product_prices (
|
||||
FOREIGN KEY(product) REFERENCES products(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cart_items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user INTEGER NOT NULL,
|
||||
product INTEGER NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
|
||||
|
||||
FOREIGN KEY(user) REFERENCES users(id),
|
||||
FOREIGN KEY(product) REFERENCES product(id)
|
||||
);
|
||||
|
||||
|
||||
|
||||
INSERT OR REPLACE INTO users (name, email, password_hash, balance_dkk_cent)
|
||||
VALUES ('User','test@email.com','08ce0220f6d63d85c3ac313e308f4fca35ecfb850baa8ddb924cfab98137b6b18b4a8e027067cb98802757df1337246a0f3aa25c44c2b788517a871086419dcf',10000);
|
||||
|
||||
|
@ -5,18 +5,28 @@
|
||||
|
||||
#define MAYBE_UNUSED __attribute__((unused))
|
||||
|
||||
#define DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||
#define DECLARE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, FN_SPECIFIER) \
|
||||
typedef TYPE VEC_TYPE##T; \
|
||||
typedef struct { \
|
||||
VEC_TYPE##T* data; \
|
||||
size_t capacity; \
|
||||
size_t size; \
|
||||
} VEC_TYPE;
|
||||
} VEC_TYPE; \
|
||||
FN_SPECIFIER int FN_PREFIX##_construct(VEC_TYPE* vec); \
|
||||
FN_SPECIFIER void FN_PREFIX##_destroy(VEC_TYPE* vec); \
|
||||
FN_SPECIFIER VEC_TYPE* FN_PREFIX##_new(void); \
|
||||
FN_SPECIFIER void FN_PREFIX##_free(VEC_TYPE* vec); \
|
||||
FN_SPECIFIER int FN_PREFIX##_push(VEC_TYPE* vec, VEC_TYPE##T value); \
|
||||
FN_SPECIFIER VEC_TYPE##T* FN_PREFIX##_at(VEC_TYPE* vec, size_t idx); \
|
||||
FN_SPECIFIER const VEC_TYPE##T* FN_PREFIX##_at_const( \
|
||||
const VEC_TYPE* vec, size_t idx); \
|
||||
FN_SPECIFIER VEC_TYPE##T FN_PREFIX##_get(const VEC_TYPE* vec, size_t idx);
|
||||
|
||||
#define DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||
MAYBE_UNUSED static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \
|
||||
#define DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, FN_SPECIFIER) \
|
||||
FN_SPECIFIER int FN_PREFIX##_construct(VEC_TYPE* vec) \
|
||||
{ \
|
||||
const size_t capacity = INITIAL_CAPACITY; \
|
||||
const size_t capacity \
|
||||
= 8 / sizeof(VEC_TYPE##T) > 2 ? 8 / sizeof(VEC_TYPE##T) : 2; \
|
||||
VEC_TYPE##T* data = malloc(sizeof(VEC_TYPE##T) * capacity); \
|
||||
if (!data) \
|
||||
return -1; \
|
||||
@ -24,12 +34,12 @@
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline void FN_PREFIX##_destroy(VEC_TYPE* vec) \
|
||||
FN_SPECIFIER void FN_PREFIX##_destroy(VEC_TYPE* vec) \
|
||||
{ \
|
||||
free(vec->data); \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline VEC_TYPE* FN_PREFIX##_new(void) \
|
||||
FN_SPECIFIER VEC_TYPE* FN_PREFIX##_new(void) \
|
||||
{ \
|
||||
VEC_TYPE* vec = malloc(sizeof(VEC_TYPE)); \
|
||||
if (!vec) \
|
||||
@ -40,14 +50,13 @@
|
||||
return vec; \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline void FN_PREFIX##_free(VEC_TYPE* vec) \
|
||||
FN_SPECIFIER void FN_PREFIX##_free(VEC_TYPE* vec) \
|
||||
{ \
|
||||
FN_PREFIX##_destroy(vec); \
|
||||
free(vec); \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline int FN_PREFIX##_push( \
|
||||
VEC_TYPE* vec, VEC_TYPE##T value) \
|
||||
FN_SPECIFIER int FN_PREFIX##_push(VEC_TYPE* vec, VEC_TYPE##T value) \
|
||||
{ \
|
||||
if (vec->size + 1 > vec->capacity) { \
|
||||
size_t new_capacity = vec->capacity * 2; \
|
||||
@ -62,29 +71,25 @@
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline VEC_TYPE##T* FN_PREFIX##_at( \
|
||||
VEC_TYPE* vec, size_t idx) \
|
||||
FN_SPECIFIER VEC_TYPE##T* FN_PREFIX##_at(VEC_TYPE* vec, size_t idx) \
|
||||
{ \
|
||||
return &vec->data[idx]; \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline const VEC_TYPE##T* FN_PREFIX##_at_const( \
|
||||
FN_SPECIFIER const VEC_TYPE##T* FN_PREFIX##_at_const( \
|
||||
const VEC_TYPE* vec, size_t idx) \
|
||||
{ \
|
||||
return &vec->data[idx]; \
|
||||
} \
|
||||
\
|
||||
MAYBE_UNUSED static inline VEC_TYPE##T FN_PREFIX##_get( \
|
||||
const VEC_TYPE* vec, size_t idx) \
|
||||
FN_SPECIFIER VEC_TYPE##T FN_PREFIX##_get(const VEC_TYPE* vec, size_t idx) \
|
||||
{ \
|
||||
return vec->data[idx]; \
|
||||
}
|
||||
|
||||
#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||
DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||
DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY)
|
||||
|
||||
#define DEFINE_MAYBE_UNUSED
|
||||
#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX) \
|
||||
DECLARE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, MAYBE_UNUSED static inline) \
|
||||
DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, MAYBE_UNUSED static inline)
|
||||
|
||||
#define DEFINE_STATIC_QUEUE(TYPE, QUEUE_TYPE, FN_PREFIX) \
|
||||
typedef struct { \
|
||||
|
@ -10,7 +10,7 @@
|
||||
MAP_TYPE##Value value; \
|
||||
} MAP_TYPE_Entry; \
|
||||
\
|
||||
DEFINE_VEC(MAP_TYPE_Entry, MAP_TYPE, FN_PREFIX##_entry_vec, 8) \
|
||||
DEFINE_VEC(MAP_TYPE_Entry, MAP_TYPE, FN_PREFIX##_entry_vec) \
|
||||
\
|
||||
MAYBE_UNUSED static inline int FN_PREFIX##_construct(MAP_TYPE* map) \
|
||||
{ \
|
||||
|
@ -1,32 +1,36 @@
|
||||
#include "../models/models_json.h"
|
||||
#include "../util/str.h"
|
||||
#include "controllers.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void route_get_cart_items_from_session(HttpCtx* ctx)
|
||||
void route_post_carts_purchase(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);
|
||||
|
||||
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);
|
||||
const char* body_str = http_ctx_req_body(ctx);
|
||||
JsonValue* body_json = json_parse(body_str, strlen(body_str));
|
||||
if (!body_json) {
|
||||
RESPOND_BAD_REQUEST(ctx, "bad request");
|
||||
return;
|
||||
}
|
||||
string_push_str(&res, "]}");
|
||||
|
||||
cart_item_vec_destroy(&cart_items);
|
||||
RESPOND_JSON(ctx, 200, "%s", res.data);
|
||||
string_destroy(&res);
|
||||
CartsPurchaseReq req;
|
||||
int parse_result = carts_purchase_req_from_json(&req, body_json);
|
||||
json_free(body_json);
|
||||
if (parse_result != 0) {
|
||||
RESPOND_BAD_REQUEST(ctx, "bad request");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("product_id\tamount\n");
|
||||
for (size_t i = 0; i < req.items.size; ++i) {
|
||||
printf("%ld\t\t%ld\n",
|
||||
req.items.data[i].product_id,
|
||||
req.items.data[i].amount);
|
||||
}
|
||||
|
||||
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ typedef struct {
|
||||
void session_construct(Session* session, int64_t user_id);
|
||||
void session_destroy(Session* session);
|
||||
|
||||
DEFINE_VEC(Session, SessionVec, session_vec, 16)
|
||||
DEFINE_VEC(Session, SessionVec, session_vec)
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t mutex;
|
||||
@ -37,7 +37,7 @@ 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_carts_purchase(HttpCtx* ctx);
|
||||
|
||||
void route_post_users_register(HttpCtx* ctx);
|
||||
|
||||
|
@ -12,8 +12,8 @@ void route_post_sessions_login(HttpCtx* ctx)
|
||||
|
||||
JsonValue* body_json = json_parse(body_str, strlen(body_str));
|
||||
|
||||
AuthLoginReq req;
|
||||
int parse_res = auth_login_req_from_json(&req, body_json);
|
||||
SessionsLoginReq req;
|
||||
int parse_res = sessions_login_req_from_json(&req, body_json);
|
||||
json_free(body_json);
|
||||
|
||||
if (parse_res != 0) {
|
||||
@ -48,7 +48,7 @@ void route_post_sessions_login(HttpCtx* ctx)
|
||||
l2_return:
|
||||
user_destroy(&user);
|
||||
l0_return:
|
||||
auth_login_req_destroy(&req);
|
||||
sessions_login_req_destroy(&req);
|
||||
}
|
||||
|
||||
void route_post_sessions_logout(HttpCtx* ctx)
|
||||
|
@ -4,9 +4,8 @@
|
||||
#include "../models/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)
|
||||
DEFINE_VEC(int64_t, Ids, ids)
|
||||
DEFINE_VEC(Product, ProductVec, product_vec)
|
||||
|
||||
typedef enum {
|
||||
DbRes_Ok,
|
||||
|
@ -21,4 +21,4 @@ typedef struct {
|
||||
char* value;
|
||||
} Header;
|
||||
|
||||
DEFINE_VEC(Header, HeaderVec, header_vec, 8)
|
||||
DEFINE_VEC(Header, HeaderVec, header_vec)
|
||||
|
@ -21,7 +21,7 @@ typedef struct {
|
||||
HttpHandlerFn handler;
|
||||
} Handler;
|
||||
|
||||
DEFINE_VEC(Handler, HandlerVec, handler_vec, 8)
|
||||
DEFINE_VEC(Handler, HandlerVec, handler_vec)
|
||||
|
||||
struct HttpServer {
|
||||
int fd;
|
||||
|
@ -11,8 +11,8 @@ typedef struct {
|
||||
JsonValue* val;
|
||||
} KV;
|
||||
|
||||
DEFINE_VEC(JsonValue*, Arr, arr, 4)
|
||||
DEFINE_VEC(KV, Obj, obj, 4)
|
||||
DEFINE_VEC(JsonValue*, Arr, arr)
|
||||
DEFINE_VEC(KV, Obj, obj)
|
||||
|
||||
struct JsonValue {
|
||||
JsonType type;
|
||||
|
@ -39,7 +39,7 @@ 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/carts/purchase", route_post_carts_purchase);
|
||||
|
||||
http_server_post(server, "/api/users/register", route_post_users_register);
|
||||
http_server_post(server, "/api/sessions/login", route_post_sessions_login);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "models.h"
|
||||
#include "../json/json.h"
|
||||
#include "../util/panic.h"
|
||||
#include "../util/str.h"
|
||||
#include "models_json.h"
|
||||
#include <assert.h>
|
||||
@ -38,13 +39,6 @@ void product_price_destroy(ProductPrice* m)
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void cart_item_destroy(CartItem* m)
|
||||
{
|
||||
static_assert(sizeof(CartItem) == 32, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void users_register_req_destroy(UsersRegisterReq* model)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
@ -54,14 +48,21 @@ void users_register_req_destroy(UsersRegisterReq* model)
|
||||
free(model->password);
|
||||
}
|
||||
|
||||
void auth_login_req_destroy(AuthLoginReq* model)
|
||||
void sessions_login_req_destroy(SessionsLoginReq* model)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
static_assert(sizeof(SessionsLoginReq) == 16, "model has changed");
|
||||
|
||||
free(model->email);
|
||||
free(model->password);
|
||||
}
|
||||
|
||||
void carts_purchase_req_destroy(CartsPurchaseReq* model)
|
||||
{
|
||||
static_assert(sizeof(CartsPurchaseReq) == 24, "model has changed");
|
||||
|
||||
carts_item_vec_destroy(&model->items);
|
||||
}
|
||||
|
||||
char* user_to_json_string(const User* m)
|
||||
{
|
||||
static_assert(sizeof(User) == 40, "model has changed");
|
||||
@ -156,29 +157,6 @@ char* product_price_to_json_string(const ProductPrice* m)
|
||||
return result;
|
||||
}
|
||||
|
||||
char* cart_item_to_json_string(const CartItem* m)
|
||||
{
|
||||
static_assert(sizeof(CartItem) == 32, "model has changed");
|
||||
|
||||
String string;
|
||||
string_construct(&string);
|
||||
string_pushf(&string,
|
||||
"{"
|
||||
"\"id\":%ld,"
|
||||
"\"user_id\":%ld,"
|
||||
"\"product_id\":%ld,"
|
||||
"\"amount\":%ld"
|
||||
"}",
|
||||
m->id,
|
||||
m->user_id,
|
||||
m->product_id,
|
||||
m->amount);
|
||||
|
||||
char* result = string_copy(&string);
|
||||
string_destroy(&string);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* users_register_req_to_json(const UsersRegisterReq* m)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
@ -200,9 +178,9 @@ char* users_register_req_to_json(const UsersRegisterReq* m)
|
||||
return result;
|
||||
}
|
||||
|
||||
char* auth_login_req_to_json(const AuthLoginReq* m)
|
||||
char* sessions_login_req_to_json(const SessionsLoginReq* m)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
static_assert(sizeof(SessionsLoginReq) == 16, "model has changed");
|
||||
|
||||
String string;
|
||||
string_construct(&string);
|
||||
@ -219,6 +197,13 @@ char* auth_login_req_to_json(const AuthLoginReq* m)
|
||||
return result;
|
||||
}
|
||||
|
||||
char* carts_purchase_req_to_json(const CartsPurchaseReq* m)
|
||||
{
|
||||
static_assert(sizeof(CartsPurchaseReq) == 24, "model has changed");
|
||||
|
||||
PANIC("not implemented");
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* key;
|
||||
JsonType type;
|
||||
@ -241,8 +226,14 @@ static inline bool obj_conforms(
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GET_INT(K) json_int(json_object_get(json, K))
|
||||
#define GET_STR(K) str_dup(json_string(json_object_get(json, K)))
|
||||
#define OBJ_GET_INT(JSON, K) json_int(json_object_get(JSON, K))
|
||||
#define OBJ_GET_STR(JSON, K) str_dup(json_string(json_object_get(JSON, K)))
|
||||
|
||||
#define OBJ_CONFORMS(JSON, FIELDS) \
|
||||
obj_conforms(JSON, FIELDS, sizeof(FIELDS) / sizeof(FIELDS[0]))
|
||||
|
||||
#define GET_INT(K) OBJ_GET_INT(json, K)
|
||||
#define GET_STR(K) OBJ_GET_STR(json, K)
|
||||
|
||||
int user_from_json(User* m, const JsonValue* json)
|
||||
{
|
||||
@ -255,7 +246,7 @@ int user_from_json(User* m, const JsonValue* json)
|
||||
{ "password_hash", JsonType_String },
|
||||
{ "balance_dkk_cent", JsonType_Number },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (User) {
|
||||
.id = GET_INT("id"),
|
||||
@ -276,7 +267,7 @@ int coord_from_json(Coord* m, const JsonValue* json)
|
||||
{ "x", JsonType_Number },
|
||||
{ "y", JsonType_Number },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (Coord) {
|
||||
.id = GET_INT("id"),
|
||||
@ -298,7 +289,7 @@ int product_from_json(Product* m, const JsonValue* json)
|
||||
{ "coord_id", JsonType_Number },
|
||||
{ "barcode", JsonType_String },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (Product) {
|
||||
.id = GET_INT("id"),
|
||||
@ -320,7 +311,7 @@ int product_price_from_json(ProductPrice* m, const JsonValue* json)
|
||||
{ "product_id", JsonType_Number },
|
||||
{ "price_dkk_cent", JsonType_Number },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (ProductPrice) {
|
||||
.id = GET_INT("id"),
|
||||
@ -330,27 +321,6 @@ int product_price_from_json(ProductPrice* m, const JsonValue* json)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cart_item_from_json(CartItem* m, const JsonValue* json)
|
||||
{
|
||||
static_assert(sizeof(CartItem) == 32, "model has changed");
|
||||
|
||||
ObjField fields[] = {
|
||||
{ "id", JsonType_Number },
|
||||
{ "user_id", JsonType_Number },
|
||||
{ "product_id", JsonType_Number },
|
||||
{ "amount", JsonType_Number },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
return -1;
|
||||
*m = (CartItem) {
|
||||
.id = GET_INT("id"),
|
||||
.user_id = GET_INT("user_id"),
|
||||
.product_id = GET_INT("product_id"),
|
||||
.amount = GET_INT("amount"),
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
@ -360,7 +330,7 @@ int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
|
||||
{ "email", JsonType_String },
|
||||
{ "password", JsonType_String },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (UsersRegisterReq) {
|
||||
.name = GET_STR("name"),
|
||||
@ -370,19 +340,60 @@ int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int auth_login_req_from_json(AuthLoginReq* m, const JsonValue* json)
|
||||
int sessions_login_req_from_json(SessionsLoginReq* m, const JsonValue* json)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
static_assert(sizeof(SessionsLoginReq) == 16, "model has changed");
|
||||
|
||||
ObjField fields[] = {
|
||||
{ "email", JsonType_String },
|
||||
{ "password", JsonType_String },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (AuthLoginReq) {
|
||||
*m = (SessionsLoginReq) {
|
||||
.email = GET_STR("email"),
|
||||
.password = GET_STR("password"),
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int carts_purchase_req_from_json(CartsPurchaseReq* m, const JsonValue* json)
|
||||
{
|
||||
static_assert(sizeof(CartsPurchaseReq) == 24, "model has changed");
|
||||
|
||||
ObjField fields[] = {
|
||||
{ "items", JsonType_Array },
|
||||
};
|
||||
if (!OBJ_CONFORMS(json, fields))
|
||||
return -1;
|
||||
*m = (CartsPurchaseReq) {
|
||||
.items = (CartsItemVec) { 0 },
|
||||
};
|
||||
carts_item_vec_construct(&m->items);
|
||||
|
||||
const JsonValue* items = json_object_get(json, "items");
|
||||
size_t items_size = json_array_size(items);
|
||||
|
||||
for (size_t i = 0; i < items_size; ++i) {
|
||||
const JsonValue* item = json_array_get(items, i);
|
||||
|
||||
ObjField item_fields[] = {
|
||||
{ "product_id", JsonType_Number },
|
||||
{ "amount", JsonType_Number },
|
||||
};
|
||||
if (!OBJ_CONFORMS(item, item_fields)) {
|
||||
carts_item_vec_destroy(&m->items);
|
||||
return -1;
|
||||
}
|
||||
|
||||
carts_item_vec_push(&m->items,
|
||||
(CartsItem) {
|
||||
.product_id = OBJ_GET_INT(item, "product_id"),
|
||||
.amount = OBJ_GET_INT(item, "amount"),
|
||||
});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_VEC_IMPL(CartsItem, CartsItemVec, carts_item_vec, )
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../collections/collection.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
@ -31,18 +32,10 @@ typedef struct {
|
||||
int64_t price_dkk_cent;
|
||||
} ProductPrice;
|
||||
|
||||
typedef struct {
|
||||
int64_t id;
|
||||
int64_t user_id;
|
||||
int64_t product_id;
|
||||
int64_t amount;
|
||||
} CartItem;
|
||||
|
||||
void user_destroy(User* model);
|
||||
void coord_destroy(Coord* model);
|
||||
void product_destroy(Product* model);
|
||||
void product_price_destroy(ProductPrice* model);
|
||||
void cart_item_destroy(CartItem* model);
|
||||
|
||||
//
|
||||
|
||||
@ -52,10 +45,24 @@ typedef struct {
|
||||
char* password;
|
||||
} UsersRegisterReq;
|
||||
|
||||
void users_register_req_destroy(UsersRegisterReq* model);
|
||||
|
||||
typedef struct {
|
||||
char* email;
|
||||
char* password;
|
||||
} AuthLoginReq;
|
||||
} SessionsLoginReq;
|
||||
|
||||
void users_register_req_destroy(UsersRegisterReq* model);
|
||||
void auth_login_req_destroy(AuthLoginReq* model);
|
||||
void sessions_login_req_destroy(SessionsLoginReq* model);
|
||||
|
||||
typedef struct {
|
||||
int64_t product_id;
|
||||
int64_t amount;
|
||||
} CartsItem;
|
||||
|
||||
DECLARE_VEC_TYPE(CartsItem, CartsItemVec, carts_item_vec, )
|
||||
|
||||
typedef struct {
|
||||
CartsItemVec items;
|
||||
} CartsPurchaseReq;
|
||||
|
||||
void carts_purchase_req_destroy(CartsPurchaseReq* model);
|
||||
|
@ -9,7 +9,7 @@ DEFINE_MODEL_JSON(User, user)
|
||||
DEFINE_MODEL_JSON(Coord, coord)
|
||||
DEFINE_MODEL_JSON(Product, product)
|
||||
DEFINE_MODEL_JSON(ProductPrice, product_price)
|
||||
DEFINE_MODEL_JSON(CartItem, cart_item)
|
||||
|
||||
DEFINE_MODEL_JSON(UsersRegisterReq, users_register_req)
|
||||
DEFINE_MODEL_JSON(AuthLoginReq, auth_login_req)
|
||||
DEFINE_MODEL_JSON(SessionsLoginReq, sessions_login_req)
|
||||
DEFINE_MODEL_JSON(CartsPurchaseReq, carts_purchase_req)
|
||||
|
@ -23,7 +23,7 @@ typedef struct {
|
||||
StrSplitter str_splitter(const char* text, size_t text_len, const char* split);
|
||||
StrSlice str_split_next(StrSplitter* splitter);
|
||||
|
||||
DEFINE_VEC(char, String, string, 8)
|
||||
DEFINE_VEC(char, String, string)
|
||||
|
||||
void string_push_str(String* string, const char* str);
|
||||
void string_push_fmt_va(String* string, const char* fmt, ...);
|
||||
@ -31,7 +31,7 @@ char* string_copy(const String* string);
|
||||
|
||||
#define string_pushf(STRING, ...) string_push_fmt_va(STRING, __VA_ARGS__)
|
||||
|
||||
DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
|
||||
DEFINE_VEC(char*, RawStrVec, rawstr_vec)
|
||||
|
||||
#define MAX_HASH_INPUT_LEN 256 - 1
|
||||
|
||||
|
100
backend/test/test.ts
Normal file
100
backend/test/test.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { assertEquals, assertMatch, assertNotEquals } from "jsr:@std/assert";
|
||||
|
||||
const url = `http://127.0.0.1:8080`;
|
||||
// const url = `http://10.135.51.114:8080`;
|
||||
|
||||
const name = "Maksim";
|
||||
const email = `mash.skp_${Math.floor(Math.random() * 100000)}@edu.mercantec.dk`;
|
||||
const password = "Merc1234";
|
||||
|
||||
Deno.test("test backend", async (t) => {
|
||||
await t.step("test /api/users/register", async () => {
|
||||
const registerRes = await post<{ ok: boolean }>(
|
||||
`/api/users/register`,
|
||||
{ name, email, password },
|
||||
);
|
||||
assertEquals(registerRes, { ok: true });
|
||||
});
|
||||
|
||||
let token: string | undefined = undefined;
|
||||
|
||||
await t.step("test /api/sessions/login", async () => {
|
||||
const loginRes = await post<{
|
||||
ok: true;
|
||||
token: string;
|
||||
}>(
|
||||
"/api/sessions/login",
|
||||
{ email, password },
|
||||
);
|
||||
|
||||
assertEquals(loginRes.ok, true);
|
||||
assertMatch(loginRes.token, /^[0-9a-zA-Z]+$/);
|
||||
token = loginRes.token;
|
||||
});
|
||||
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
await t.step("test /api/sessions/user", async () => {
|
||||
const sessionUserRes = await get<{
|
||||
ok: boolean;
|
||||
user: unknown;
|
||||
}>(
|
||||
"/api/sessions/user",
|
||||
{ "Session-Token": token! },
|
||||
);
|
||||
|
||||
assertEquals(sessionUserRes.ok, true);
|
||||
// console.log(sessionUserRes.user);
|
||||
});
|
||||
|
||||
await testCarts(t, token);
|
||||
|
||||
await t.step("test /api/sessions/logout", async () => {
|
||||
const logoutRes = await post<{ ok: boolean }>(
|
||||
"/api/sessions/logout",
|
||||
{},
|
||||
{ "Session-Token": token! },
|
||||
);
|
||||
|
||||
assertEquals(logoutRes, { ok: true });
|
||||
});
|
||||
});
|
||||
|
||||
async function testCarts(t: Deno.TestContext, token: string) {
|
||||
await t.step("test /api/carts/purchase", async () => {
|
||||
const res = await post<{ ok: boolean }>(
|
||||
"/api/carts/purchase",
|
||||
{
|
||||
items: [
|
||||
{ product_id: 1, amount: 2 },
|
||||
{ product_id: 2, amount: 5 },
|
||||
],
|
||||
},
|
||||
{ "Session-Token": token },
|
||||
);
|
||||
|
||||
assertEquals(res, { ok: true });
|
||||
});
|
||||
}
|
||||
|
||||
function get<Res>(
|
||||
path: string,
|
||||
headers: Record<string, string>,
|
||||
): Promise<Res> {
|
||||
return fetch(`${url}${path}`, { headers })
|
||||
.then((res) => res.json());
|
||||
}
|
||||
|
||||
function post<Res, Req = unknown>(
|
||||
path: string,
|
||||
body: Req,
|
||||
headers: Record<string, string> = {},
|
||||
): Promise<Res> {
|
||||
return fetch(`${url}${path}`, {
|
||||
method: "post",
|
||||
headers: { ...headers, "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
}).then((res) => res.json());
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import { assertEquals, assertMatch } from "jsr:@std/assert";
|
||||
|
||||
const url = `http://127.0.0.1:8080`;
|
||||
// const url = `http://10.135.51.114:8080`;
|
||||
|
||||
const name = "Maksim";
|
||||
const email = `mash.skp_${Math.floor(Math.random() * 100000)}@edu.mercantec.dk`;
|
||||
const password = "Merc1234";
|
||||
|
||||
Deno.test("test", async () => {
|
||||
const registerRes = await post<{ ok: boolean }>(
|
||||
`/api/users/register`,
|
||||
{ name, email, password },
|
||||
);
|
||||
|
||||
assertEquals(registerRes, { ok: true });
|
||||
|
||||
const loginRes = await post<{
|
||||
ok: true;
|
||||
token: string;
|
||||
}>(
|
||||
"/api/sessions/login",
|
||||
{ email, password },
|
||||
);
|
||||
|
||||
assertEquals(loginRes.ok, true);
|
||||
assertMatch(loginRes.token, /^[0-9a-zA-Z]+$/);
|
||||
const token = loginRes.token;
|
||||
|
||||
const sessionUserRes = await get<{
|
||||
ok: boolean;
|
||||
user: unknown;
|
||||
}>(
|
||||
"/api/sessions/user",
|
||||
{ "Session-Token": token },
|
||||
);
|
||||
|
||||
assertEquals(sessionUserRes.ok, true);
|
||||
console.log(sessionUserRes.user);
|
||||
|
||||
const logoutRes = await post<{ ok: boolean }>(
|
||||
"/api/sessions/logout",
|
||||
{},
|
||||
{ "Session-Token": token },
|
||||
);
|
||||
|
||||
assertEquals(logoutRes, { ok: true });
|
||||
});
|
||||
|
||||
function get<Res>(
|
||||
path: string,
|
||||
headers: Record<string, string>,
|
||||
): Promise<Res> {
|
||||
return fetch(`${url}${path}`, { headers })
|
||||
.then((res) => res.json());
|
||||
}
|
||||
|
||||
function post<Res, Req = unknown>(
|
||||
path: string,
|
||||
body: Req,
|
||||
headers: Record<string, string> = {},
|
||||
): Promise<Res> {
|
||||
return fetch(`${url}${path}`, {
|
||||
method: "post",
|
||||
headers: { ...headers, "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
}).then((res) => res.json());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user