diff --git a/spec/openapi.yaml b/spec/openapi.yaml index 17c0cc251d4c848b6f16ee92a17c27c14407fe13..f08e27167b44a49ad0fd68efcbe2417ef0990011 100644 --- a/spec/openapi.yaml +++ b/spec/openapi.yaml @@ -143,6 +143,29 @@ paths: description: Error while retrieving data. deprecated: false + /account/profile/{name}/cache: + delete: + tags: + - User Profile + summary: Clear user profile cache + description: Clear the cache data for specified user. + operationId: clearCacheForUser + security: + - OAuth2: + - eclipsefdn_view_all_profiles + parameters: + - name: name + in: path + description: An ef username. + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Success + /account/profile/{name}/eca: get: tags: diff --git a/src/main/java/org/eclipsefoundation/profile/resources/AccountResource.java b/src/main/java/org/eclipsefoundation/profile/resources/AccountResource.java index 63471278d3bf10f02a085560f6722714e4a7d67d..e08f42f62e3533383d8f52bf69898238175330fa 100644 --- a/src/main/java/org/eclipsefoundation/profile/resources/AccountResource.java +++ b/src/main/java/org/eclipsefoundation/profile/resources/AccountResource.java @@ -34,10 +34,12 @@ import org.eclipsefoundation.profile.services.ProfileService; import org.eclipsefoundation.profile.services.UserDeleteService; import org.eclipsefoundation.utils.helper.TransformationHelper; import org.jboss.resteasy.reactive.NoCache; +import org.jboss.resteasy.reactive.RestResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.POST; @@ -138,6 +140,14 @@ public class AccountResource { return Response.ok(getCachedProfileByUsername(username)).build(); } + @DELETE + @AuthenticatedAlternate + @Path("account/profile/{username}/cache") + public RestResponse<Void> clearCacheForUser(@PathParam("username") String username) { + cache.fuzzyRemove(username, EfUser.class); + return RestResponse.ok(); + } + /** * Returns a 200 OK Response containing a given user's ECA status. If the user is not found, an ECA with all false values is returned. * diff --git a/src/test/java/org/eclipsefoundation/profile/resources/AccountResourceTest.java b/src/test/java/org/eclipsefoundation/profile/resources/AccountResourceTest.java index 9f5d0b87ec5cbccd936eb5ee4186500d74c26b58..01607245cb6f7c7f5b461e91874e954aa7f7081e 100644 --- a/src/test/java/org/eclipsefoundation/profile/resources/AccountResourceTest.java +++ b/src/test/java/org/eclipsefoundation/profile/resources/AccountResourceTest.java @@ -16,6 +16,8 @@ import java.util.List; import java.util.Map; import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipsefoundation.caching.service.CachingService; +import org.eclipsefoundation.efservices.api.models.EfUser; import org.eclipsefoundation.http.model.FlatRequestWrapper; import org.eclipsefoundation.http.model.RequestWrapper; import org.eclipsefoundation.persistence.dao.impl.DefaultHibernateDao; @@ -48,6 +50,7 @@ class AccountResourceTest { public static final String USER_SEARCH_UID_URL = BASE_URL + "?uid={param}"; public static final String USER_SEARCH_NAME_URL = BASE_URL + "?name={param}"; public static final String PROFILE_BY_USER_URL = BASE_URL + "/{username}"; + public static final String CACHE_URL = PROFILE_BY_USER_URL + "/cache"; public static final String ECA_URL = PROFILE_BY_USER_URL + "/eca"; public static final String EMPLOYMENT_HISTORY_URL = PROFILE_BY_USER_URL + "/employment-history"; public static final String MAILING_LIST_URL = PROFILE_BY_USER_URL + "/mailing-list"; @@ -181,6 +184,18 @@ class AccountResourceTest { .setHeaderParams(TestNamespaceHelper.VALID_ANON_AUTH_HEADER) .build(); + /* + * Cache clear + */ + public static final EndpointTestCase CLEAR_USER_CACHE_USER_PRESENT = TestCaseHelper + .prepareTestCase(CACHE_URL, new String[] { TestNamespaceHelper.VALID_USERNAME }, null) + .setHeaderParams(TestNamespaceHelper.VALID_ANON_AUTH_HEADER) + .build(); + public static final EndpointTestCase CLEAR_USER_CACHE_NO_USER = TestCaseHelper + .prepareTestCase(CACHE_URL, new String[] { TestNamespaceHelper.INVALID_USERNAME }, null) + .setHeaderParams(TestNamespaceHelper.VALID_ANON_AUTH_HEADER) + .build(); + /* * ECA */ @@ -256,6 +271,8 @@ class AccountResourceTest { DefaultHibernateDao defaultDao; @Inject FilterService filters; + @Inject + CachingService cache; @ConfigProperty(name = "eclipse.profile.user-delete.hosts") Map<String, String> hosts; @@ -565,6 +582,65 @@ class AccountResourceTest { EndpointTestBuilder.from(GET_BY_USER_SUCCESS_BAD_AUTH).andCheckFormat().run(); } + /* + * HTTP DELETE + * + * /account/profile/:username/cache + */ + @Test + void testClearCacheForUser_failure_noAuth() { + // ensure user is cached + EndpointTestBuilder.from(GET_BY_USER_CASE_SUCCESS_AUTH).run(); + assertUserCached(TestNamespaceHelper.VALID_USERNAME); + + // No auth header + EndpointTestBuilder + .from(TestCaseHelper.buildForbiddenCase(CACHE_URL, new String[] { TestNamespaceHelper.VALID_USERNAME }, null)) + .doDelete() + .run(); + + // user should still be cached + assertUserCached(TestNamespaceHelper.VALID_USERNAME); + } + + @Test + void testClearCacheForUser_failure_invalidAuth() { + // ensure user is cached + EndpointTestBuilder.from(GET_BY_USER_CASE_SUCCESS_AUTH).run(); + assertUserCached(TestNamespaceHelper.VALID_USERNAME); + // Valid but anonymous auth token + EndpointTestBuilder + .from(TestCaseHelper + .prepareTestCase(CACHE_URL, new String[] { TestNamespaceHelper.VALID_USERNAME }, null) + .setHeaderParams(TestNamespaceHelper.INVALID_ANON_AUTH_HEADER) + .setStatusCode(403) + .build()) + .doDelete() + .run(); + + // user should still be cached + assertUserCached(TestNamespaceHelper.VALID_USERNAME); + } + + @Test + void testClearCacheForUser_success() { + // ensure user is cached + EndpointTestBuilder.from(GET_BY_USER_CASE_SUCCESS_AUTH).run(); + assertUserCached(TestNamespaceHelper.VALID_USERNAME); + + EndpointTestBuilder.from(CLEAR_USER_CACHE_USER_PRESENT).doDelete().run(); + // check that the cache is properly cleared + assertUserNotCached(TestNamespaceHelper.VALID_USERNAME); + } + + @Test + void testClearCacheForUser_success_noUser() { + // run the cache clear, expect a 200 even for a missing user + EndpointTestBuilder.from(CLEAR_USER_CACHE_NO_USER).doDelete().run(); + // there should be no user cached + assertUserNotCached(TestNamespaceHelper.INVALID_USERNAME); + } + /* * GET /account/profile/:username/eca */ @@ -809,4 +885,16 @@ class AccountResourceTest { .setStatusCode(409) .build(); } + + private void assertUserCached(String username) { + Assertions + .assertTrue(cache.getCacheKeys().stream().anyMatch(k -> k.clazz() == EfUser.class && k.id().equals(username)), + "Cache should have a user with username " + username); + } + + private void assertUserNotCached(String username) { + Assertions + .assertFalse(cache.getCacheKeys().stream().anyMatch(k -> k.clazz() == EfUser.class && k.id().equals(username)), + "Cache should not have a user with username " + username); + } }