Skip to content

Commit

Permalink
🐛 Include alias cache purge w/ status refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
ebullient committed Jun 20, 2024
1 parent 6c0a5a8 commit aa2e245
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;

import org.commonhaus.automation.admin.api.CommonhausUser.ForwardEmail;
import org.commonhaus.automation.admin.api.CommonhausUser.Services;
import org.commonhaus.automation.admin.forwardemail.Alias;
import org.commonhaus.automation.admin.forwardemail.AliasKey;
import org.commonhaus.automation.admin.forwardemail.ForwardEmailService;
Expand Down Expand Up @@ -51,16 +48,12 @@ public class MemberAliasesResource {
@GET
@KnownUser
@Produces("application/json")
public Response getAliases(@DefaultValue("false") @QueryParam("refresh") boolean refresh) {
public Response getAliases() {
try {
CommonhausUser user = getUser();
Services services = user.services();
ForwardEmail emailConfig = services.forwardEmail();

Set<AliasKey> emailAddresses = emailService.normalizeEmailAddresses(session, emailConfig);

// API CALL: get alias mappings
Map<AliasKey, Alias> aliasMap = emailService.fetchAliases(emailAddresses, refresh);
Map<AliasKey, Alias> aliasMap = emailService.fetchAliases(session, user);

// Return as map of string / alias
return user.toResponse()
Expand All @@ -85,8 +78,7 @@ public Response updateAliases(Map<String, Set<String>> aliases) {
CommonhausUser user = getUser();
ForwardEmail emailConfig = user.services().forwardEmail();

Set<AliasKey> emailAddresses = emailService.normalizeEmailAddresses(session, emailConfig);
Map<AliasKey, Set<String>> sanitized = emailService.sanitizeInputAddresses(aliases, emailAddresses);
Map<AliasKey, Set<String>> sanitized = emailService.sanitizeInputAddresses(session, user, aliases);
if (sanitized.isEmpty()) {
Log.debugf("[%s] updateAliases: No valid email addresses to update: %s", session.login(), aliases.keySet());
return Response.status(Response.Status.NO_CONTENT).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;

import org.commonhaus.automation.admin.forwardemail.ForwardEmailService;
import org.commonhaus.automation.admin.github.AppContextService;
import org.commonhaus.automation.admin.github.CommonhausDatastore;
import org.commonhaus.automation.admin.github.CommonhausDatastore.UpdateEvent;
Expand All @@ -39,6 +40,9 @@ public class MemberResource {
@Inject
MemberSession session;

@Inject
ForwardEmailService emailService;

@GET
@Path("/github")
@Produces("application/json")
Expand Down Expand Up @@ -107,14 +111,17 @@ public Response updateUserStatus(@DefaultValue("false") @QueryParam("refresh") b
if (refresh) {
// reset all the things.
session.forgetUser(ctx);

// re-fetch the user
session.userIsKnown(ctx);
Log.debugf("[%s] REFRESH /member/commonhaus/status %s", session.login(), session.roles());
}

try {
CommonhausUser user = datastore.getCommonhausUser(session, refresh, false);
if (refresh) {
emailService.forgetUser(session, user);
}

final Set<String> roles = session.roles();
if (user.statusUpdateRequired(ctx, roles)) {
// Refresh the user's status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import jakarta.ws.rs.core.Response.Status;

import org.commonhaus.automation.admin.AdminDataCache;
import org.commonhaus.automation.admin.api.CommonhausUser;
import org.commonhaus.automation.admin.api.CommonhausUser.ForwardEmail;
import org.commonhaus.automation.admin.api.CommonhausUser.Services;
import org.commonhaus.automation.admin.api.MemberSession;
import org.commonhaus.automation.admin.config.UserManagementConfig;
import org.commonhaus.automation.admin.github.AppContextService;
Expand All @@ -31,6 +33,14 @@ public class ForwardEmailService {
@Inject
AppContextService ctx;

public Map<AliasKey, Alias> fetchAliases(MemberSession session, CommonhausUser user) {
Set<AliasKey> emailAddresses = getConfiguredAliases(session, user);
if (emailAddresses.isEmpty()) {
return Map.of();
}
return fetchAliases(emailAddresses);
}

/**
* Fetch aliases using the ForwardEmail Rest Client
*
Expand All @@ -40,15 +50,15 @@ public class ForwardEmailService {
* @throws WebApplicationException on Rest Client error (including Not Found)
* @see #getAlias(AliasKey, boolean)
*/
public Map<AliasKey, Alias> fetchAliases(Set<AliasKey> emails, boolean resetCache) {
public Map<AliasKey, Alias> fetchAliases(Set<AliasKey> emails) {
if (emailDisabled()) {
return Map.of();
}
Map<AliasKey, Alias> aliases = new HashMap<>();
for (AliasKey key : emails) {
try {
// API CALL: will throw WebApplicationException if not found or error
Alias alias = getAlias(key, resetCache);
Alias alias = getAlias(key);
aliases.put(key, alias);
} catch (WebApplicationException e) {
if (e.getResponse().getStatus() == 404) {
Expand Down Expand Up @@ -76,7 +86,7 @@ public Map<AliasKey, Alias> postAliases(Map<AliasKey, Set<String>> aliases, Stri
return Map.of();
}
Map<AliasKey, Alias> result = new HashMap<>();
Map<AliasKey, Alias> existingAliases = fetchAliases(aliases.keySet(), false);
Map<AliasKey, Alias> existingAliases = fetchAliases(aliases.keySet());
for (Map.Entry<AliasKey, Set<String>> entry : aliases.entrySet()) {
try {
AliasKey key = entry.getKey();
Expand Down Expand Up @@ -120,6 +130,17 @@ public boolean generatePassword(String email) {
return false;
}

/**
* Clear the cache of all aliases configured for the specified user
*
* @param session MemberSession
* @param user CommonhausUser
*/
public void forgetUser(MemberSession session, CommonhausUser user) {
Set<AliasKey> emailAddresses = getConfiguredAliases(session, user);
emailAddresses.forEach(x -> AdminDataCache.ALIASES.invalidate(x.toString()));
}

/**
* Wrap the call the ForwardEmailClient.getAlias to cache the result
*
Expand All @@ -129,12 +150,12 @@ public boolean generatePassword(String email) {
* @throws WebApplicationException on Rest Client error (including Not Found)
* or if the key resolves to multiple aliases
*/
protected Alias getAlias(@Nonnull AliasKey aliasKey, boolean resetCache) {
protected Alias getAlias(@Nonnull AliasKey aliasKey) {
if (emailDisabled()) {
return null;
}
String lookup = aliasKey.toString();
Alias alias = resetCache ? null : AdminDataCache.ALIASES.get(lookup);
Alias alias = AdminDataCache.ALIASES.get(lookup);
if (alias == null) {
// API CALL: will throw WebApplicationException if not found or error
// Will throw 404 on not found
Expand Down Expand Up @@ -227,4 +248,28 @@ public Map<AliasKey, Set<String>> sanitizeInputAddresses(Map<String, Set<String>
});
return sanitized;
}

public Set<AliasKey> getConfiguredAliases(MemberSession session, CommonhausUser user) {
if (!user.status().mayHaveEmail() || emailDisabled()) {
return Set.of();
}
Services services = user.services();
ForwardEmail emailConfig = services.forwardEmail();
return normalizeEmailAddresses(session, emailConfig);
}

public Map<AliasKey, Set<String>> sanitizeInputAddresses(MemberSession session, CommonhausUser user,
Map<String, Set<String>> input) {
Set<AliasKey> permitted = getConfiguredAliases(session, user);

// Filter/Remove any unknown/extraneous email addresses
Map<AliasKey, Set<String>> sanitized = new HashMap<>();
input.entrySet().forEach(x -> {
AliasKey address = normalizeAlias(x.getKey());
if (permitted.contains(address)) {
sanitized.put(address, x.getValue());
}
});
return sanitized;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;

import org.commonhaus.automation.admin.AdminDataCache;
import org.commonhaus.automation.admin.dev.ForwardEmailTestEndpoint;
import org.commonhaus.automation.admin.dev.ForwardEmailTestEndpoint.TestAlias;
import org.commonhaus.automation.admin.github.AppContextService;
Expand All @@ -27,6 +28,7 @@
@QuarkusTest
public class ForwardEmailTest {

@Inject
@RestClient
ForwardEmailClient forwardEmailClient;

Expand All @@ -42,6 +44,7 @@ public class ForwardEmailTest {
@BeforeEach
public void setup() {
testEndpoint.clear();
AdminDataCache.ALIASES.invalidateAll();
}

/**
Expand Down Expand Up @@ -132,8 +135,7 @@ public void testAliasNotExist() throws Exception {
ContextHelper.setUserManagementConfig(ctx);
// This should not throw: 404 should be handled (empty response)
forwardEmailService.fetchAliases(
Set.of(AliasKey.fromCache("not_found@commonhaus.dev")),
true);
Set.of(AliasKey.fromCache("not_found@commonhaus.dev")));
var methodCalls = testEndpoint.getMethodCalls();
assertThat(methodCalls).size().isEqualTo(1);

Expand All @@ -148,8 +150,7 @@ public void testAliasError() throws Exception {
ContextHelper.setUserManagementConfig(ctx);
assertThrows(WebApplicationException.class, () -> {
forwardEmailService.fetchAliases(
Set.of(AliasKey.fromCache("error@commonhaus.dev")),
true);
Set.of(AliasKey.fromCache("error@commonhaus.dev")));
});
var methodCalls = testEndpoint.getMethodCalls();
assertThat(methodCalls).size().isEqualTo(1);
Expand All @@ -164,8 +165,7 @@ public void testAliasError() throws Exception {
public void testQueryAliases() throws Exception {
ContextHelper.setUserManagementConfig(ctx);
Map<AliasKey, Alias> aliases = forwardEmailService.fetchAliases(
Set.of(AliasKey.fromCache("test@commonhaus.dev")),
true);
Set.of(AliasKey.fromCache("test@commonhaus.dev")));
assertThat(aliases).size().isEqualTo(1);

var methodCalls = testEndpoint.getMethodCalls();
Expand Down

0 comments on commit aa2e245

Please sign in to comment.