module transaction.api; import handy_http_primitives; import handy_http_data.json; import handy_http_handlers.path_handler; import transaction.model; import transaction.data; import transaction.service; import profile.data; import profile.service; import util.money; import util.pagination; import util.data; immutable DEFAULT_TRANSACTION_PAGE = PageRequest(0, 10, [Sort("created_at", SortDir.DESC)]); struct TransactionResponse { import std.typecons : Nullable, nullable; ulong id; string timestamp; string addedAt; ulong amount; string currency; string description; Nullable!ulong vendorId; Nullable!ulong categoryId; static TransactionResponse of(in Transaction tx) { return TransactionResponse( tx.id, tx.timestamp.toISOExtString(), tx.addedAt.toISOExtString(), tx.amount, tx.currency.code.idup, tx.description, tx.vendorId.toNullable, tx.categoryId.toNullable ); } } void getTransactions(ref ServerHttpRequest request, ref ServerHttpResponse response) { ProfileDataSource ds = getProfileDataSource(request); PageRequest pr = PageRequest.parse(request, DEFAULT_TRANSACTION_PAGE); Page!Transaction page = ds.getTransactionRepository().findAll(pr); writeJsonBody(response, page.mapItems(&TransactionResponse.of)); } void getVendors(ref ServerHttpRequest request, ref ServerHttpResponse response) { ProfileDataSource ds = getProfileDataSource(request); auto vendorRepo = ds.getTransactionVendorRepository(); TransactionVendor[] vendors = vendorRepo.findAll(); writeJsonBody(response, vendors); } void getVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) { long vendorId = request.getPathParamAs!long("vendorId", -1); if (vendorId == -1) { response.status = HttpStatus.NOT_FOUND; response.writeBodyString("Missing vendorId path parameter."); return; } ProfileDataSource ds = getProfileDataSource(request); auto vendorRepo = ds.getTransactionVendorRepository(); TransactionVendor vendor = vendorRepo.findById(vendorId) .orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND)); writeJsonBody(response, vendor); } struct VendorPayload { string name; string description; } void createVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) { VendorPayload payload = readJsonBodyAs!VendorPayload(request); ProfileDataSource ds = getProfileDataSource(request); auto vendorRepo = ds.getTransactionVendorRepository(); if (vendorRepo.existsByName(payload.name)) { response.status = HttpStatus.BAD_REQUEST; response.writeBodyString("Vendor name is already in use."); return; } TransactionVendor vendor = vendorRepo.insert(payload.name, payload.description); writeJsonBody(response, vendor); } void updateVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) { VendorPayload payload = readJsonBodyAs!VendorPayload(request); long vendorId = request.getPathParamAs!long("vendorId", -1); if (vendorId == -1) { response.status = HttpStatus.NOT_FOUND; response.writeBodyString("Missing vendorId path parameter."); return; } ProfileDataSource ds = getProfileDataSource(request); auto vendorRepo = ds.getTransactionVendorRepository(); TransactionVendor existingVendor = vendorRepo.findById(vendorId) .orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND)); if (payload.name != existingVendor.name && vendorRepo.existsByName(payload.name)) { response.status = HttpStatus.BAD_REQUEST; response.writeBodyString("Vendor name is already in use."); return; } TransactionVendor updated = vendorRepo.updateById( vendorId, payload.name, payload.description ); writeJsonBody(response, updated); } void deleteVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) { long vendorId = request.getPathParamAs!long("vendorId", -1); if (vendorId == -1) { response.status = HttpStatus.NOT_FOUND; response.writeBodyString("Missing vendorId path parameter."); return; } ProfileDataSource ds = getProfileDataSource(request); auto vendorRepo = ds.getTransactionVendorRepository(); vendorRepo.deleteById(vendorId); }