199 lines
8.1 KiB
D
199 lines
8.1 KiB
D
module transaction.api;
|
|
|
|
import handy_http_primitives;
|
|
import handy_http_data.json;
|
|
import handy_http_handlers.path_handler;
|
|
import slf4d;
|
|
import std.typecons;
|
|
|
|
import transaction.model;
|
|
import transaction.data;
|
|
import transaction.service;
|
|
import transaction.dto;
|
|
import profile.data;
|
|
import profile.service;
|
|
import profile.api : PROFILE_PATH;
|
|
import account.api;
|
|
import util.money;
|
|
import util.pagination;
|
|
import util.data;
|
|
|
|
// Transactions API
|
|
|
|
immutable DEFAULT_TRANSACTION_PAGE = PageRequest(1, 10, [Sort("txn.timestamp", SortDir.DESC)]);
|
|
|
|
@PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/transactions")
|
|
void handleGetTransactions(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
PageRequest pr = PageRequest.parse(request, DEFAULT_TRANSACTION_PAGE);
|
|
auto responsePage = getTransactions(ds, pr);
|
|
writeJsonBody(response, responsePage);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/transactions/search")
|
|
void handleSearchTransactions(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
PageRequest pr = PageRequest.parse(request, DEFAULT_TRANSACTION_PAGE);
|
|
auto page = ds.getTransactionRepository().search(pr, request);
|
|
writeJsonBody(response, page);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/transactions/:transactionId:ulong")
|
|
void handleGetTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
TransactionDetail txn = getTransaction(ds, getTransactionIdOrThrow(request));
|
|
import asdf : serializeToJson;
|
|
string jsonStr = serializeToJson(txn);
|
|
response.writeBodyString(jsonStr, "application/json");
|
|
}
|
|
|
|
@PathMapping(HttpMethod.POST, PROFILE_PATH ~ "/transactions")
|
|
void handleAddTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
import asdf : serializeToJson;
|
|
auto fullPayload = parseMultipartFilesAndBody!AddTransactionPayload(request);
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
TransactionDetail txn = addTransaction(ds, fullPayload.payload, fullPayload.files);
|
|
string jsonStr = serializeToJson(txn);
|
|
response.writeBodyString(jsonStr, "application/json");
|
|
}
|
|
|
|
@PathMapping(HttpMethod.PUT, PROFILE_PATH ~ "/transactions/:transactionId:ulong")
|
|
void handleUpdateTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
import asdf : serializeToJson;
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
ulong txnId = getTransactionIdOrThrow(request);
|
|
auto fullPayload = parseMultipartFilesAndBody!AddTransactionPayload(request);
|
|
TransactionDetail txn = updateTransaction(ds, txnId, fullPayload.payload, fullPayload.files);
|
|
string jsonStr = serializeToJson(txn);
|
|
response.writeBodyString(jsonStr, "application/json");
|
|
}
|
|
|
|
@PathMapping(HttpMethod.DELETE, PROFILE_PATH ~ "/transactions/:transactionId:ulong")
|
|
void handleDeleteTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
ulong txnId = getTransactionIdOrThrow(request);
|
|
deleteTransaction(ds, txnId);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/transaction-tags")
|
|
void handleGetAllTags(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
string[] tags = ds.getTransactionTagRepository().findAll();
|
|
writeJsonBody(response, tags);
|
|
}
|
|
|
|
private ulong getTransactionIdOrThrow(in ServerHttpRequest request) {
|
|
return getPathParamOrThrow!ulong(request, "transactionId");
|
|
}
|
|
|
|
// Vendors API
|
|
|
|
@PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/vendors")
|
|
void handleGetVendors(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
TransactionVendor[] vendors = getAllVendors(ds);
|
|
writeJsonBody(response, vendors);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/vendors/:vendorId:ulong")
|
|
void handleGetVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
TransactionVendor vendor = getVendor(ds, getVendorId(request));
|
|
writeJsonBody(response, vendor);
|
|
}
|
|
|
|
struct VendorPayload {
|
|
string name;
|
|
string description;
|
|
}
|
|
|
|
@PathMapping(HttpMethod.POST, PROFILE_PATH ~ "/vendors")
|
|
void handleCreateVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
VendorPayload payload = readJsonBodyAs!VendorPayload(request);
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
TransactionVendor vendor = createVendor(ds, payload);
|
|
writeJsonBody(response, vendor);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.PUT, PROFILE_PATH ~ "/vendors/:vendorId:ulong")
|
|
void handleUpdateVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
VendorPayload payload = readJsonBodyAs!VendorPayload(request);
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
TransactionVendor updated = updateVendor(ds, getVendorId(request), payload);
|
|
writeJsonBody(response, updated);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.DELETE, PROFILE_PATH ~ "/vendors/:vendorId:ulong")
|
|
void handleDeleteVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
deleteVendor(ds, getVendorId(request));
|
|
}
|
|
|
|
private ulong getVendorId(in ServerHttpRequest request) {
|
|
return getPathParamOrThrow!ulong(request, "vendorId");
|
|
}
|
|
|
|
// Categories API
|
|
|
|
@GetMapping(PROFILE_PATH ~ "/categories")
|
|
void handleGetCategories(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
TransactionCategoryTree[] categories = getCategories(getProfileDataSource(request));
|
|
writeJsonBody(response, categories);
|
|
}
|
|
|
|
@GetMapping(PROFILE_PATH ~ "/categories/:categoryId:ulong")
|
|
void handleGetCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
auto category = getCategory(getProfileDataSource(request), getCategoryId(request));
|
|
writeJsonBody(response, category);
|
|
}
|
|
|
|
@GetMapping(PROFILE_PATH ~ "/categories/:categoryId:ulong/children")
|
|
void handleGetChildCategories(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
auto children = getChildCategories(getProfileDataSource(request), getCategoryId(request));
|
|
writeJsonBody(response, children);
|
|
}
|
|
|
|
@GetMapping(PROFILE_PATH ~ "/categories/:categoryId:ulong/balances")
|
|
void handleGetCategoryBalances(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
response.status = HttpStatus.NOT_IMPLEMENTED;
|
|
// TODO: Add an API endpoint to provide a "balance" for the category.
|
|
// This would be the sum of credits and debits for all transactions set
|
|
// to this category or any child of it, over a specified interval, or for
|
|
// some default interval if none is provided.
|
|
}
|
|
|
|
struct CategoryPayload {
|
|
string name;
|
|
string description;
|
|
string color;
|
|
Nullable!ulong parentId;
|
|
}
|
|
|
|
@PathMapping(HttpMethod.POST, PROFILE_PATH ~ "/categories")
|
|
void handleCreateCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
CategoryPayload payload = readJsonBodyAs!CategoryPayload(request);
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
auto category = createCategory(ds, payload);
|
|
writeJsonBody(response, category);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.PUT, PROFILE_PATH ~ "/categories/:categoryId:ulong")
|
|
void handleUpdateCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
CategoryPayload payload = readJsonBodyAs!CategoryPayload(request);
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
ulong categoryId = getCategoryId(request);
|
|
auto category = updateCategory(ds, categoryId, payload);
|
|
writeJsonBody(response, category);
|
|
}
|
|
|
|
@PathMapping(HttpMethod.DELETE, PROFILE_PATH ~ "/categories/:categoryId:ulong")
|
|
void handleDeleteCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
ulong categoryId = getCategoryId(request);
|
|
deleteCategory(ds, categoryId);
|
|
}
|
|
|
|
private ulong getCategoryId(in ServerHttpRequest request) {
|
|
return getPathParamOrThrow!ulong(request, "categoryId");
|
|
}
|