module transaction.api; import handy_http_primitives; import handy_http_data.json; import handy_http_handlers.path_handler; import slf4d; 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(1, 10, [Sort("timestamp", SortDir.DESC)]); struct TransactionResponse { import asdf : serdeTransformOut; ulong id; string timestamp; string addedAt; ulong amount; string currency; string description; @serdeTransformOut!serializeOptional Optional!ulong vendorId; @serdeTransformOut!serializeOptional Optional!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, tx.categoryId ); } } 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); Page!TransactionResponse responsePage = page.mapTo!()(&TransactionResponse.of); writeJsonBody(response, responsePage); } 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); }