189 lines
6.7 KiB
D
189 lines
6.7 KiB
D
/**
|
|
* This module defines the API endpoints for dealing with Accounts directly,
|
|
* including any data-transfer objects that are needed.
|
|
*/
|
|
module account.api;
|
|
|
|
import handy_http_primitives;
|
|
import handy_http_data.json;
|
|
import handy_http_handlers.path_handler;
|
|
import std.datetime;
|
|
|
|
import profile.service;
|
|
import profile.data;
|
|
import account.model;
|
|
import account.service;
|
|
import util.money;
|
|
import util.pagination;
|
|
import account.data;
|
|
|
|
/// The data the API provides for an Account entity.
|
|
struct AccountResponse {
|
|
import asdf : serdeTransformOut;
|
|
import util.data;
|
|
|
|
ulong id;
|
|
string createdAt;
|
|
bool archived;
|
|
string type;
|
|
string numberSuffix;
|
|
string name;
|
|
string currency;
|
|
string description;
|
|
@serdeTransformOut!serializeOptional
|
|
Optional!long currentBalance;
|
|
|
|
static AccountResponse of(in Account account, Optional!long currentBalance) {
|
|
AccountResponse r;
|
|
r.id = account.id;
|
|
r.createdAt = account.createdAt.toISOExtString();
|
|
r.archived = account.archived;
|
|
r.type = account.type.id;
|
|
r.numberSuffix = account.numberSuffix;
|
|
r.name = account.name;
|
|
r.currency = account.currency.code.dup;
|
|
r.description = account.description;
|
|
r.currentBalance = currentBalance;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
void handleGetAccounts(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
import std.algorithm;
|
|
import std.array;
|
|
auto ds = getProfileDataSource(request);
|
|
auto accounts = ds.getAccountRepository().findAll()
|
|
.map!(a => AccountResponse.of(a, getBalance(ds, a.id))).array;
|
|
writeJsonBody(response, accounts);
|
|
}
|
|
|
|
void handleGetAccount(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
auto ds = getProfileDataSource(request);
|
|
auto account = ds.getAccountRepository().findById(accountId)
|
|
.orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
|
|
writeJsonBody(response, AccountResponse.of(account, getBalance(ds, account.id)));
|
|
}
|
|
|
|
// The data provided by a user to create a new account.
|
|
struct AccountCreationPayload {
|
|
string type;
|
|
string numberSuffix;
|
|
string name;
|
|
string currency;
|
|
string description;
|
|
}
|
|
|
|
void handleCreateAccount(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
auto ds = getProfileDataSource(request);
|
|
AccountCreationPayload payload = readJsonBodyAs!AccountCreationPayload(request);
|
|
// TODO: Validate the account creation payload.
|
|
AccountType type = AccountType.fromId(payload.type);
|
|
Currency currency = Currency.ofCode(payload.currency);
|
|
Account account = ds.getAccountRepository().insert(
|
|
type,
|
|
payload.numberSuffix,
|
|
payload.name,
|
|
currency,
|
|
payload.description
|
|
);
|
|
writeJsonBody(response, AccountResponse.of(account, getBalance(ds, account.id)));
|
|
}
|
|
|
|
void handleUpdateAccount(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
AccountCreationPayload payload = readJsonBodyAs!AccountCreationPayload(request);
|
|
auto ds = getProfileDataSource(request);
|
|
AccountRepository repo = ds.getAccountRepository();
|
|
auto account = repo.findById(accountId).orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
|
|
Account updated = repo.update(accountId, Account(
|
|
account.id,
|
|
account.createdAt,
|
|
account.archived,
|
|
AccountType.fromId(payload.type),
|
|
payload.numberSuffix,
|
|
payload.name,
|
|
Currency.ofCode(payload.currency),
|
|
payload.description
|
|
));
|
|
writeJsonBody(response, AccountResponse.of(updated, getBalance(ds, updated.id)));
|
|
}
|
|
|
|
void handleDeleteAccount(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
auto ds = getProfileDataSource(request);
|
|
ds.getAccountRepository().deleteById(accountId);
|
|
}
|
|
|
|
const PageRequest VALUE_RECORD_DEFAULT_PAGE_REQUEST = PageRequest(1, 10, [Sort("timestamp", SortDir.DESC)]);
|
|
|
|
struct AccountValueRecordResponse {
|
|
ulong id;
|
|
string timestamp;
|
|
ulong accountId;
|
|
string type;
|
|
long value;
|
|
Currency currency;
|
|
|
|
static AccountValueRecordResponse of(in AccountValueRecord vr) {
|
|
return AccountValueRecordResponse(
|
|
vr.id,
|
|
vr.timestamp.toISOExtString(),
|
|
vr.accountId,
|
|
vr.type,
|
|
vr.value,
|
|
vr.currency
|
|
);
|
|
}
|
|
}
|
|
|
|
void handleGetValueRecords(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
auto ds = getProfileDataSource(request);
|
|
auto page = ds.getAccountValueRecordRepository()
|
|
.findAllByAccountId(accountId, PageRequest.parse(request, VALUE_RECORD_DEFAULT_PAGE_REQUEST))
|
|
.mapTo!()(&AccountValueRecordResponse.of);
|
|
writeJsonBody(response, page);
|
|
}
|
|
|
|
void handleGetValueRecord(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
ulong valueRecordId = request.getPathParamAs!ulong("valueRecordId");
|
|
auto ds = getProfileDataSource(request);
|
|
auto record = ds.getAccountValueRecordRepository().findById(accountId, valueRecordId)
|
|
.orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
|
|
writeJsonBody(response, AccountValueRecordResponse.of(record));
|
|
}
|
|
|
|
struct ValueRecordCreationPayload {
|
|
string timestamp;
|
|
string type;
|
|
long value;
|
|
}
|
|
|
|
void handleCreateValueRecord(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
Account account = ds.getAccountRepository().findById(accountId)
|
|
.orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
|
|
ValueRecordCreationPayload payload = readJsonBodyAs!ValueRecordCreationPayload(request);
|
|
SysTime timestamp = SysTime.fromISOExtString(payload.timestamp);
|
|
AccountValueRecordType type = AccountValueRecordType.BALANCE; // TODO: Support more types.
|
|
AccountValueRecord record = ds.getAccountValueRecordRepository().insert(
|
|
timestamp,
|
|
account.id,
|
|
type,
|
|
payload.value,
|
|
account.currency
|
|
);
|
|
writeJsonBody(response, AccountValueRecordResponse.of(record));
|
|
}
|
|
|
|
void handleDeleteValueRecord(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
|
ulong valueRecordId = request.getPathParamAs!ulong("valueRecordId");
|
|
ProfileDataSource ds = getProfileDataSource(request);
|
|
ds.getAccountValueRecordRepository()
|
|
.deleteById(accountId, valueRecordId);
|
|
}
|