finnow/finnow-api/source/profile/service.d

86 lines
2.8 KiB
D

module profile.service;
import handy_httpd;
import handy_httpd.components.optional;
import profile.model;
import profile.data;
import profile.data_impl_sqlite;
import auth.model;
/**
* Validates a profile name.
* Params:
* name = The name to check.
* Returns: True if the profile name is valid.
*/
bool validateProfileName(string name) {
import std.regex;
import std.uni : toLower;
if (name is null || name.length < 3) return false;
auto r = ctRegex!(`^[a-zA-Z]+[a-zA-Z0-9_]+$`);
return !matchFirst(name, r).empty;
}
/// Contextual information that's available when handling requests under a profile.
struct ProfileContext {
const Profile profile;
const User user;
}
/**
* Tries to get a profile context from a request context. This will attempt to
* extract a "profile" path parameter and the authenticated user, and combine
* them into the ProfileContext.
* Params:
* ctx = The request context to read.
* Returns: An optional profile context.
*/
Optional!ProfileContext getProfileContext(in HttpRequestContext ctx) {
import auth.service : AuthContext, getAuthContext;
if ("profile" !in ctx.request.pathParams) return Optional!ProfileContext.empty;
string profileName = ctx.request.pathParams["profile"];
if (!validateProfileName(profileName)) return Optional!ProfileContext.empty;
AuthContext authCtx = getAuthContext(ctx);
if (authCtx is null) return Optional!ProfileContext.empty;
User user = authCtx.user;
ProfileRepository repo = new FileSystemProfileRepository(user.username);
return repo.findByName(profileName)
.mapIfPresent!(p => ProfileContext(p, user));
}
/**
* Similar to `getProfileContext`, but throws an HttpStatusException with a
* 404 NOT FOUND status if no profile context could be obtained.
* Params:
* ctx = The request context to read.
* Returns: The profile context that was obtained.
*/
ProfileContext getProfileContextOrThrow(in HttpRequestContext ctx) {
return getProfileContext(ctx).orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
}
/**
* Obtains a ProfileDataSource from a ProfileContext. Use this to get access
* to all profile data when handling an HTTP request, for example.
* Params:
* pc = The profile context.
* Returns: The profile data source.
*/
ProfileDataSource getProfileDataSource(in ProfileContext pc) {
ProfileRepository profileRepo = new FileSystemProfileRepository(pc.user.username);
return profileRepo.getDataSource(pc.profile);
}
/**
* Obtains a ProfileDataSource from an HTTP request context. Use this to
* access all profile data when handling the request.
* Params:
* ctx = The request context.
* Returns: The profile data source.
*/
ProfileDataSource getProfileDataSource(in HttpRequestContext ctx) {
ProfileContext pc = getProfileContextOrThrow(ctx);
return getProfileDataSource(pc);
}