diff --git a/analysis-service/src/main/java/net/explorviz/analysis/Main.java b/analysis-service/src/main/java/net/explorviz/analysis/Main.java index 9871f9fb..c9488cc7 100644 --- a/analysis-service/src/main/java/net/explorviz/analysis/Main.java +++ b/analysis-service/src/main/java/net/explorviz/analysis/Main.java @@ -2,10 +2,17 @@ import net.explorviz.kiekeradapter.main.KiekerAdapter; -public class Main { +/** + * Main class for analysis-service. + */ +public final class Main { + + private Main(){} public static void main(final String[] args) { KiekerAdapter.getInstance().startReader(); } + + } diff --git a/conf/checkstyle.xml b/conf/checkstyle.xml index 791e5fab..55fa9b82 100644 --- a/conf/checkstyle.xml +++ b/conf/checkstyle.xml @@ -295,6 +295,7 @@ + @@ -313,7 +314,9 @@ - + + + @@ -322,6 +325,10 @@ + + + + diff --git a/discovery-service/src/main/java/net/explorviz/discovery/repository/discovery/AgentRepository.java b/discovery-service/src/main/java/net/explorviz/discovery/repository/discovery/AgentRepository.java index f3c690c6..962895ee 100644 --- a/discovery-service/src/main/java/net/explorviz/discovery/repository/discovery/AgentRepository.java +++ b/discovery-service/src/main/java/net/explorviz/discovery/repository/discovery/AgentRepository.java @@ -12,6 +12,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * In-Memory repository for {@link Agent}s. + */ public class AgentRepository { private static final Logger LOGGER = LoggerFactory.getLogger(AgentRepository.class); @@ -22,19 +25,34 @@ public class AgentRepository { private final BroadcastService broadcastService; + /** + * Creates a new repository. Handled by DI. + * @param broadcastService the used + */ @Inject public AgentRepository(final BroadcastService broadcastService) { this.broadcastService = broadcastService; } - public String getUniqueIdString() { + private String getUniqueIdString() { return String.valueOf(this.idGenerator.incrementAndGet()); } + /** + * Get all agents. + * + * @return list of all registered agents. + */ public List getAgents() { return this.agents; } + /** + * Find a specific agent. + * + * @param agent the agent to search for. + * @return the agent if it exists in this repository or {@code null} otherwise + */ public Agent lookupAgent(final Agent agent) { synchronized (this.agents) { return this.agents.stream() @@ -45,6 +63,13 @@ public Agent lookupAgent(final Agent agent) { } } + /** + * Register a new agent. If the agent to register already existed, it is re-registered with the + * same Id. + * + * @param agent the agent to register + * @return the registered agent + */ public Agent registerAgent(final Agent agent) { synchronized (this.agents) { final Agent possibleOldAgent = this.lookupAgent(agent); @@ -69,6 +94,12 @@ public Agent registerAgent(final Agent agent) { return agent; } + /** + * Generates and assigns a unique Id for each process in a list. + * + * @param procezzList the list of {@link Procezz} objects + * @return the same list of procezzes where each object has an Id now + */ public List insertIdsInProcezzList(final List procezzList) { for (final Procezz p : procezzList) { @@ -79,6 +110,12 @@ public List insertIdsInProcezzList(final List procezzList) { } + /** + * Find an agent by its Id. + * + * @param id the Id to look for + * @return An optional containing the agent with the given Id if found + */ public Optional lookupAgentById(final String id) { synchronized (this.agents) { for (final Agent agent : this.agents) { @@ -91,6 +128,10 @@ public Optional lookupAgentById(final String id) { return Optional.empty(); } + /** + * Updates an existing agent. Fails silently if such agent does not exist. + * @param a the agent to update + */ public void updateAgent(final Agent a) { synchronized (this.agents) { final Agent potentialAgent = this.lookupAgent(a); @@ -106,6 +147,7 @@ public void updateAgent(final Agent a) { } } + public void exchangeProcezzInAgent(final Procezz proc, final Agent a) { synchronized (this.agents) { diff --git a/discovery-service/src/main/java/net/explorviz/discovery/server/main/Application.java b/discovery-service/src/main/java/net/explorviz/discovery/server/main/Application.java index f92e3ff1..6e1c6eed 100644 --- a/discovery-service/src/main/java/net/explorviz/discovery/server/main/Application.java +++ b/discovery-service/src/main/java/net/explorviz/discovery/server/main/Application.java @@ -23,7 +23,7 @@ class Application extends ResourceConfig { public Application() { - + super(); GenericTypeFinder.getTypeMap().put("Agent", Agent.class); GenericTypeFinder.getTypeMap().put("Procezz", Procezz.class); diff --git a/discovery-service/src/main/java/net/explorviz/discovery/server/resources/AgentResource.java b/discovery-service/src/main/java/net/explorviz/discovery/server/resources/AgentResource.java index d1a72c3b..61b5529f 100644 --- a/discovery-service/src/main/java/net/explorviz/discovery/server/resources/AgentResource.java +++ b/discovery-service/src/main/java/net/explorviz/discovery/server/resources/AgentResource.java @@ -52,21 +52,39 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +/** + * Resource class to interact with {@link Agent}s. + */ @SecurityScheme(type = SecuritySchemeType.HTTP, name = "token", scheme = "bearer", - bearerFormat = "JWT") + bearerFormat = "JWT") @Path("v1/agents") public class AgentResource { private static final Logger LOGGER = LoggerFactory.getLogger(AgentResource.class); + private static final String NO_AGENT_MSG = "No agent for this process is registered."; + + + private static final String MEDIA_TYPE = "application/vnd.api+json"; private static final MediaType JSON_API_TYPE = new MediaType("application", "vnd.api+json"); private static final int UNPROCESSABLE_ENTITY = 422; + private static final String AGENT_BASE_URL = "agentBaseURL"; + private static final String AGENT_PATH = "agentAgentPath"; private final AgentRepository agentRepository; private final ResourceConverter converter; private final ClientService clientService; + + /** + * Creates a new resource. Handled by DI. + * + * @param converter the resource converter + * @param agentRepository the repository to access agents + * @param clientService the service to access agents via HTTP + */ @Inject public AgentResource(final ResourceConverter converter, final AgentRepository agentRepository, final ClientService clientService) { @@ -78,12 +96,19 @@ public AgentResource(final ResourceConverter converter, final AgentRepository ag this.clientService.registerProviderWriter(new JsonApiProvider<>(converter)); } + /** + * Handles the endpoint clients use to register for agent updates. + * + * @param sse injected entry point for server sent events + * @param agentBroadcastSubResource injected sub-resource to handle the request + * @return the sub-resource which handles the request + */ @Tag(name = "Agent Broadcast") @Operation(description = "Endpoint that clients can use to register for agent updates.", - summary = "Register for agent updates") + summary = "Register for agent updates") @ApiResponse(description = "Updated agent objects with procezzes of the discovery agents.", - content = @Content(mediaType = MediaType.SERVER_SENT_EVENTS, - schema = @Schema(implementation = Agent.class))) + content = @Content(mediaType = MediaType.SERVER_SENT_EVENTS, + schema = @Schema(implementation = Agent.class))) @SecurityRequirement(name = "token") @Secure @PermitAll @@ -93,50 +118,61 @@ public AgentBroadcastSubResource getAgentBroadcastResource(@Context final Sse ss return agentBroadcastSubResource; } + /** + * Handles requests to obtain the list of processes for an agent. + * + * @param agentId the ID of the agent + * @return List of processes associated with the given agent + * @throws AgentNotFoundException if an agent with given ID does not exist + */ @Operation(description = "Endpoint that clients can use to obtain all procezzes for an agent id.", - summary = "Get procezzes for agent id.") + summary = "Get procezzes for agent id.") @ApiResponse(description = "List of procezzes for the passed agent id.", - content = @Content(mediaType = MediaType.SERVER_SENT_EVENTS, - schema = @Schema(implementation = Procezz.class))) + content = @Content(mediaType = MediaType.SERVER_SENT_EVENTS, + schema = @Schema(implementation = Procezz.class))) @SecurityRequirement(name = "token") @Secure @PermitAll @Path("{id}/procezzes") public ProcezzResource getProcezzResource( - @Parameter(description = "Id of the agent.") @PathParam("id") final String agentID) + @Parameter(description = "Id of the agent.") @PathParam("id") final String agentId) throws AgentNotFoundException { - final Optional agentOptional = this.agentRepository.lookupAgentById(agentID); + final Optional agentOptional = this.agentRepository.lookupAgentById(agentId); if (agentOptional.isPresent()) { return new ProcezzResource(this.clientService, this.agentRepository); } else { - throw new WebApplicationException("No agent for this process is registered.", - UNPROCESSABLE_ENTITY); + throw new WebApplicationException(NO_AGENT_MSG, UNPROCESSABLE_ENTITY); } } - @Operation( - description = "Endpoint that discovery agents can use to register, so that the frontend will get their data.", - summary = "Register discovery agent.") + /** + * Handles the registration of new discovery agents. + * * + * + * @param newAgent the agent to register + * @return the registered agent + */ + @Operation(description = "Endpoint that discovery agents can use to register," + + " so that the frontend will get their data.", summary = "Register discovery agent.") @ApiResponse(description = "List of procezzes for the passed agent id.", - content = @Content(mediaType = MediaType.SERVER_SENT_EVENTS, - schema = @Schema(implementation = Procezz.class))) + content = @Content(mediaType = MediaType.SERVER_SENT_EVENTS, + schema = @Schema(implementation = Procezz.class))) @RequestBody(description = "TODO", - content = @Content(schema = @Schema(implementation = Agent.class))) + content = @Content(schema = @Schema(implementation = Agent.class))) @POST @Consumes(MEDIA_TYPE) - public Agent registerAgent(final Agent newAgent) throws DocumentSerializationException { + public Agent registerAgent(final Agent newAgent) { // Attention, registration of MessageBodyReader implementation (JsonApiProvier) is mandatory - final Client client = ClientBuilder.newBuilder() - .register(SseFeature.class) - .register(new JsonApiProvider<>(this.converter)) - .build(); - final WebTarget target = - client.target("http://" + newAgent.getIP() + ":" + newAgent.getPort() + "/broadcast/"); + final Client client = ClientBuilder.newBuilder().register(SseFeature.class) + .register(new JsonApiProvider<>(this.converter)).build(); + + final String url = buildUrlForAgent(newAgent, "broadcast/"); + final WebTarget target = client.target(url); final EventSource eventSource = EventSource.target(target).build(); final EventListener listener = new EventListener() { @@ -157,32 +193,38 @@ public void onEvent(final InboundEvent inboundEvent) { return this.agentRepository.registerAgent(newAgent); } + /** + * Handles requests to update agents. + * + * @param agentId id of the agent to update + * @param agent the agent entity containing the updates + * @return the updated agent + * @throws AgentInternalErrorException if updated failed + * @throws AgentNoConnectionException if no connection to the agent could be established + */ @Operation(summary = "Update an agent") @ApiResponse(responseCode = "422", description = "No agent with the given id exists.") @ApiResponse(responseCode = "200", - description = "Update successful, response contains the updated agent.", - content = @Content(schema = @Schema(implementation = Agent.class))) + description = "Update successful, response contains the updated agent.", + content = @Content(schema = @Schema(implementation = Agent.class))) @RequestBody(description = "TODO", - content = @Content(schema = @Schema(implementation = Agent.class))) + content = @Content(schema = @Schema(implementation = Agent.class))) @PATCH @Path("{id}") @Consumes(MEDIA_TYPE) public Agent patchAgent( - @Parameter(description = "Id of th agent.") @PathParam("id") final String agentID, + @Parameter(description = "Id of th agent.") @PathParam("id") final String agentId, final Agent agent) throws AgentInternalErrorException, AgentNoConnectionException { - final Optional agentOptional = this.agentRepository.lookupAgentById(agentID); + final Optional agentOptional = this.agentRepository.lookupAgentById(agentId); if (!agentOptional.isPresent()) { - throw new WebApplicationException("No agent for this process is registered.", - UNPROCESSABLE_ENTITY); + throw new WebApplicationException(NO_AGENT_MSG, UNPROCESSABLE_ENTITY); } - final String urlPath = PropertyHelper.getStringProperty("agentBaseURL") - + PropertyHelper.getStringProperty("agentAgentPath"); - - final String ipAndPort = agent.getIP() + ":" + agent.getPort(); - final String url = "http://" + ipAndPort + urlPath; + final String urlPath = PropertyHelper.getStringProperty(AGENT_BASE_URL) + PropertyHelper + .getStringProperty(AGENT_PATH); + final String url = buildUrlForAgent(agent, urlPath); // See RFC5789 page 4 for appropriate status codes @@ -192,10 +234,10 @@ public Agent patchAgent( @Operation(summary = "Update an agent") @ApiResponse(responseCode = "422", description = "No agent with the given id exists.") @ApiResponse(responseCode = "200", - description = "Update successful, response contains the updated agent.", - content = @Content(schema = @Schema(implementation = Agent.class))) + description = "Update successful, response contains the updated agent.", + content = @Content(schema = @Schema(implementation = Agent.class))) @RequestBody(description = "TODO", - content = @Content(schema = @Schema(implementation = Agent.class))) + content = @Content(schema = @Schema(implementation = Agent.class))) @SecurityRequirement(name = "token") @GET @Produces(MEDIA_TYPE) @@ -205,17 +247,15 @@ public Response forwardAgentListRequest() throws DocumentSerializationException final List agentList = this.agentRepository.getAgents(); - final String urlPath = PropertyHelper.getStringProperty("agentBaseURL") - + PropertyHelper.getStringProperty("agentAgentPath"); + final String urlPath = PropertyHelper.getStringProperty(AGENT_BASE_URL) + PropertyHelper + .getStringProperty(AGENT_PATH); for (final Agent agent : agentList) { if (agent.getId() == null) { continue; } - - final String ipAndPort = agent.getIP() + ":" + agent.getPort(); - final String url = "http://" + ipAndPort + urlPath; + final String url = buildUrlForAgent(agent, urlPath); try { final Agent agentObject = this.clientService.doGETRequest(Agent.class, url, null); @@ -233,8 +273,18 @@ public Response forwardAgentListRequest() throws DocumentSerializationException return Response .ok(this.converter.writeDocumentCollection(new JSONAPIDocument<>(listToBeReturned))) - .type(MEDIA_TYPE) - .build(); + .type(MEDIA_TYPE).build(); + + } + + + private String buildUrlForAgent(final Agent agent, final String path) { + + final StringBuilder sb = new StringBuilder(); + sb.append("http://").append(agent.getIP()).append(':').append(agent.getPort()).append('/') + .append(path); + + return sb.toString(); } diff --git a/discovery-service/src/main/java/net/explorviz/discovery/server/resources/ProcezzResource.java b/discovery-service/src/main/java/net/explorviz/discovery/server/resources/ProcezzResource.java index aa23ca9c..b7872ba5 100644 --- a/discovery-service/src/main/java/net/explorviz/discovery/server/resources/ProcezzResource.java +++ b/discovery-service/src/main/java/net/explorviz/discovery/server/resources/ProcezzResource.java @@ -33,6 +33,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Resource to access processes associated to agents. + */ @Secure @PermitAll public class ProcezzResource { @@ -51,6 +54,7 @@ public ProcezzResource(final ClientService clientService, final AgentRepository this.clientService = clientService; } + @PATCH @Path("{id}") @Consumes(MEDIA_TYPE) @@ -81,12 +85,12 @@ public List insertIdsInProcezzList(final List procezzList) { } private Response forwardPatchRequest(final Procezz procezz, final String urlPath) - throws ProcezzGenericException, AgentNotFoundException, AgentNoConnectionException { + throws ProcezzGenericException, AgentNoConnectionException { final Optional agentOptional = this.agentRepository.lookupAgentById(procezz.getAgent().getId()); - if (!agentOptional.isPresent()) { + if (agentOptional.isEmpty()) { throw new WebApplicationException("No agent for this process is registered.", UNPROCESSABLE_ENTITY); } diff --git a/discovery-service/src/main/java/net/explorviz/discovery/server/services/BroadcastService.java b/discovery-service/src/main/java/net/explorviz/discovery/server/services/BroadcastService.java index 96043f4e..f572ca35 100644 --- a/discovery-service/src/main/java/net/explorviz/discovery/server/services/BroadcastService.java +++ b/discovery-service/src/main/java/net/explorviz/discovery/server/services/BroadcastService.java @@ -13,6 +13,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Service to broadcast messages to via server sent events. + */ @Service @Singleton public class BroadcastService { @@ -20,10 +23,16 @@ public class BroadcastService { private static final Logger LOGGER = LoggerFactory.getLogger(BroadcastService.class); private static final MediaType APPLICATION_JSON_API_TYPE = new MediaType("application", "vnd.api+json"); + private static final String MESSAGE = "message"; private final Sse sse; private final SseBroadcaster broadcaster; + /** + * Creates a new service. Handled by DI. + * + * @param sse the SSE provider + */ public BroadcastService(@Context final Sse sse) { this.sse = sse; this.broadcaster = sse.newBroadcaster(); @@ -32,10 +41,15 @@ public BroadcastService(@Context final Sse sse) { this.broadcaster.onError(this::onErrorOperation); } + /** + * Broadcast a list of agents. + * + * @param agentList the list of agents to broadcast the message to + */ public void broadcastMessage(final List agentList) { LOGGER.info("Broadcasting SSE"); final OutboundSseEvent event = this.sse.newEventBuilder() - .name("message") + .name(MESSAGE) .mediaType(APPLICATION_JSON_API_TYPE) .data(agentList) .build(); @@ -43,11 +57,17 @@ public void broadcastMessage(final List agentList) { this.broadcaster.broadcast(event); } + /** + * Register a new subscriber and dispatch broadcast. + * + * @param eventSink the SSE sink to register + * @param agentList the agents to send + */ public void register(final SseEventSink eventSink, final List agentList) { this.broadcaster.register(eventSink); final OutboundSseEvent event = this.sse.newEventBuilder() - .name("message") + .name(MESSAGE) .mediaType(APPLICATION_JSON_API_TYPE) .data(agentList) .build(); @@ -61,9 +81,12 @@ private void onCloseOperation(final SseEventSink sink) { // NOPMD } private void onErrorOperation(final SseEventSink sink, final Throwable e) { // NOPMD - LOGGER.error( - "Broadcasting to a SseEventSink failed. This may not be a problem, since there is no way to unregister.", - e); + if (LOGGER.isErrorEnabled()) { + LOGGER.error( + "Broadcasting to a SseEventSink failed. This may not be a problem, " + + "since there is no way to unregister.", + e); + } } } diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/DummyLandscapeHelper.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/DummyLandscapeHelper.java index a1b12fa5..b17917c0 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/DummyLandscapeHelper.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/DummyLandscapeHelper.java @@ -21,7 +21,10 @@ */ public final class DummyLandscapeHelper { - public static IdGenerator idGen; + // CHECKSTYLE.OFF: MultipleStringLiteralsCheck - Much more readable than NOCS in many lines + // CHECKSTYLE.OFF: MagicNumberCheck - Much more readable than NOCS in many lines + + public static IdGenerator idGen; //NOCS private DummyLandscapeHelper() { // Utility Class @@ -52,7 +55,7 @@ public static int getNextSequenceId() { } /** - * Creates a new system + * Creates a new system. * * @param name - name of the system * @param parentLandscape - parent landscape @@ -72,7 +75,7 @@ public static System createSystem(final String name, final Landscape parentLands } /** - * Creates a new nodeGroup + * Creates a new nodeGroup. * * @param name - name of the nodeGroup * @param system - parent system @@ -86,7 +89,7 @@ public static NodeGroup createNodeGroup(final String name, final System system) } /** - * Creates a new node + * Creates a new node. * * @param ipAddress - ipAddress of the node * @param parentNodeGroup - parent nodeGroup @@ -100,8 +103,8 @@ public static Node createNode(final String ipAddress, final NodeGroup parentNode // set random usage node.setCpuUtilization((double) getRandomNum(10, 100) / 100); - node.setFreeRAM((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); - node.setUsedRAM((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); + node.setFreeRam((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); + node.setUsedRam((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); // create a new node event landscape.createNewEvent(idGen.generateId(), @@ -113,7 +116,7 @@ public static Node createNode(final String ipAddress, final NodeGroup parentNode } /** - * Creates a new application + * Creates a new application. * * @param name - name of the application * @param parentNode - name of the parent node @@ -147,7 +150,7 @@ public static Application createApplication(final String name, final Node parent } /** - * Create communication between applications + * Create communication between applications. * * @param source - sourceApplication * @param target - targetApplication @@ -172,7 +175,7 @@ public static ApplicationCommunication createApplicationCommunication(final Appl } /** - * Creates a component + * Creates a component. * * @param name - name of the component * @param parent - parent component @@ -195,7 +198,7 @@ public static Component createComponent(final String name, final Component paren } /** - * Creates a clazz + * Creates a clazz. * * @param name - name of the clazz * @param component - parent component @@ -215,7 +218,7 @@ public static Clazz createClazz(final String name, final Component component, } /** - * Creating a communication between two clazzes within the dummy landscape + * Creating a communication between two clazzes within the dummy landscape. * * @param traceId - id of the trace * @param requests - number of requests diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/LandscapeDummyCreator.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/LandscapeDummyCreator.java index 30adfc43..881d5a78 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/LandscapeDummyCreator.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/LandscapeDummyCreator.java @@ -20,8 +20,8 @@ public final class LandscapeDummyCreator { // CHECKSTYLE.OFF: MultipleStringLiteralsCheck - Much more readable than NOCS in many lines // CHECKSTYLE.OFF: MagicNumberCheck - Much more readable than NOCS in many lines - public static int applicationId = 0; - public static int formatFactor = 1024 * 1024 * 1024; + public static int applicationId = 0; // NOCS + public static int formatFactor = 1024 * 1024 * 1024; // NOCS private static IdGenerator idGen; @@ -32,7 +32,7 @@ private LandscapeDummyCreator() { /** - * Create a dummy landscape for demo and mocking purposes + * Create a dummy landscape for demo and mocking purposes. * * @return a prepared dummy landscape */ @@ -314,17 +314,10 @@ public static Landscape createDummyLandscape(final IdGenerator idGen) { } /** - * <<<<<<< HEAD Creates a dummy webshop application within the dummy landscape + * Creates a dummy webshop application within the dummy landscape. * - * @param application - * @return webshop application ======= Creating a communication between two clazzes within the - * dummy landscape. - * - * @param traceId the id of the trace - * @param requests the request - * @param sourceClazz the source class - * @param targetClazz the target class - * @param application the appliaction >>>>>>> dev-1 + * @param application the base application + * @return webshop application */ private static Application createWebshopApplication(final Application application) { @@ -456,9 +449,9 @@ private static Application createWebshopApplication(final Application applicatio } /** - * Creates a dummy database-query including application within the dummy landscape + * Creates a dummy database-query including application within the dummy landscape. * - * @param application + * @param application the application * @return database connector application */ private static Application createDatabaseConnector(final Application application) { diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepositoryTest.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepositoryTest.java index dbd059d9..5e413860 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepositoryTest.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepositoryTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; + import java.util.Optional; import java.util.Random; import javax.inject.Inject; @@ -47,8 +48,9 @@ public void setUp() { final DependencyInjectionBinder binder = new DependencyInjectionBinder(); final ServiceLocator locator = ServiceLocatorUtilities.bind(binder); locator.inject(this); + } else { + this.repo.clear(); } - this.repo.clear(); } diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepositoryTest.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepositoryTest.java index 6a6968f8..2c0f14f8 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepositoryTest.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepositoryTest.java @@ -1,6 +1,7 @@ package net.explorviz.history.repository.persistence.mongo; import static org.junit.Assert.assertEquals; + import java.util.Optional; import java.util.Random; import javax.inject.Inject; @@ -17,10 +18,12 @@ /** * Tests the {@link MongoLandscapeRepositoryTest}. Expects a running mongodb server. - * */ public class MongoLandscapeRepositoryTest { + + + public static final String IDS_DONT_MATCH = "Ids don't match"; @Inject private MongoLandscapeRepository repo; @@ -41,8 +44,9 @@ public void setUp() { final DependencyInjectionBinder binder = new DependencyInjectionBinder(); final ServiceLocator locator = ServiceLocatorUtilities.bind(binder); locator.inject(this); + } else { + this.repo.clear(); } - this.repo.clear(); } @@ -61,8 +65,8 @@ public void findByTimestamp() { final Optional landscapeRetrieved = this.repo.getByTimestamp(ts); - assertEquals("Ids don't match", landscape.getId(), landscapeRetrieved.get().getId()); // NOPMD - // NOCS + assertEquals(IDS_DONT_MATCH, landscape.getId(), landscapeRetrieved.get().getId()); // NOPMD + // NOCS } @@ -75,7 +79,8 @@ public void testTotalRequets() { final Optional landscapeRetrieved = this.repo.getByTimestamp(ts); - assertEquals("Requests don't match", landscape.getId(), landscapeRetrieved.get().getId()); // NOPMD + assertEquals("Requests don't match", landscape.getId(), + landscapeRetrieved.get().getId()); // NOPMD } @Test @@ -90,7 +95,7 @@ public void testFindById() { final Optional landscapeRetrieved = this.repo.getByTimestamp(ts); - assertEquals("Ids don't match", id, landscapeRetrieved.get().getId()); + assertEquals(IDS_DONT_MATCH, id, landscapeRetrieved.get().getId()); } diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepositoryTest.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepositoryTest.java index 81299e86..75b0f69b 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepositoryTest.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepositoryTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; + import java.util.Optional; import java.util.Random; import javax.inject.Inject; @@ -45,8 +46,9 @@ public void setUp() { final DependencyInjectionBinder binder = new DependencyInjectionBinder(); final ServiceLocator locator = ServiceLocatorUtilities.bind(binder); locator.inject(this); + } else { + this.repo.clear(); } - this.repo.clear(); } @After diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepositoryTest.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepositoryTest.java index 2b0276fb..41b7d5ee 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepositoryTest.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepositoryTest.java @@ -1,6 +1,7 @@ package net.explorviz.history.repository.persistence.mongo; import static org.junit.Assert.assertEquals; + import java.util.Optional; import java.util.Random; import javax.inject.Inject; @@ -44,8 +45,9 @@ public void setUp() { final DependencyInjectionBinder binder = new DependencyInjectionBinder(); final ServiceLocator locator = ServiceLocatorUtilities.bind(binder); locator.inject(this); + } else { + this.repo.clear(); } - this.repo.clear(); } diff --git a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/TimestampRepositoryTest.java b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/TimestampRepositoryTest.java index 07fbc28d..57c77000 100644 --- a/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/TimestampRepositoryTest.java +++ b/history-service/src/integrationTest/java/net/explorviz/history/repository/persistence/mongo/TimestampRepositoryTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; + import java.util.Collection; import javax.inject.Inject; import javax.ws.rs.core.MultivaluedHashMap; @@ -18,9 +19,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +/** + * Tests the time {@link TimestampRepository}, expects a running MongoDB. + */ public class TimestampRepositoryTest { + // CHECKSTYLE.OFF: MultipleStringLiteralsCheck - Much more readable than NOCS in many lines + // CHECKSTYLE.OFF: MagicNumberCheck - Much more readable than NOCS in many lines + public static final String FILTER_TYPE = "filter[type]"; @Inject private MongoLandscapeRepository landscapeRepo; @Inject @@ -35,6 +42,9 @@ public static void setUpAll() { HistoryApplication.registerLandscapeModels(); } + /** + * Injects the dependencies. + */ @BeforeEach public void setUp() { if (this.timestampRepo == null) { @@ -87,7 +97,7 @@ public void testQueryForLandscapes() throws QueryException { this.addReplays(); final MultivaluedHashMap paramters = new MultivaluedHashMap<>(); - paramters.add("filter[type]", "landscape"); + paramters.add(FILTER_TYPE, "landscape"); final Query q = Query.fromParameterMap(paramters); final Collection result = this.timestampRepo.query(q).getData(); assertEquals(2, result.size()); @@ -99,7 +109,7 @@ public void testQueryForReplays() throws QueryException { this.addReplays(); final MultivaluedHashMap paramters = new MultivaluedHashMap<>(); - paramters.add("filter[type]", "replay"); + paramters.add(FILTER_TYPE, "replay"); final Query q = Query.fromParameterMap(paramters); final Collection result = this.timestampRepo.query(q).getData(); assertEquals(2, result.size()); @@ -118,22 +128,21 @@ public void testQueryForAll() throws QueryException { @Test public void testQueryFrom() throws QueryException { + final Landscape landscape = LandscapeDummyCreator.createDummyLandscape(this.idGenerator); - final long ts = 1l; final Landscape landscape2 = LandscapeDummyCreator.createDummyLandscape(this.idGenerator); - final long ts2 = 2l; final Landscape landscape3 = LandscapeDummyCreator.createDummyLandscape(this.idGenerator); - final long ts3 = 3l; - this.landscapeRepo.save(ts, landscape, 0); - this.landscapeRepo.save(ts2, landscape2, 0); - this.landscapeRepo.save(ts3, landscape3, 0); + + this.landscapeRepo.save(1L, landscape, 0); + this.landscapeRepo.save(2L, landscape2, 0); + this.landscapeRepo.save(3L, landscape3, 0); final MultivaluedHashMap paramters = new MultivaluedHashMap<>(); paramters.add("filter[from]", "2"); final Query q = Query.fromParameterMap(paramters); final Collection result = this.timestampRepo.query(q).getData(); - assertEquals(2l, result.size()); + assertEquals(2L, result.size()); } diff --git a/history-service/src/main/java/net/explorviz/history/kafka/KafkaLandscapeExchangeService.java b/history-service/src/main/java/net/explorviz/history/kafka/KafkaLandscapeExchangeService.java index 6feb9cc9..4294e17c 100644 --- a/history-service/src/main/java/net/explorviz/history/kafka/KafkaLandscapeExchangeService.java +++ b/history-service/src/main/java/net/explorviz/history/kafka/KafkaLandscapeExchangeService.java @@ -89,7 +89,7 @@ public void run() { } this.mongoLandscapeRepo - .save(l.getTimestamp().getTimestamp(), l, l.getTimestamp().getTotalRequests()); + .save(l.getTimestamp().getUnixTimestamp(), l, l.getTimestamp().getTotalRequests()); } } diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoHelper.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoHelper.java index a5fd748b..e1fe8e9d 100644 --- a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoHelper.java +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoHelper.java @@ -29,7 +29,8 @@ public final class MongoHelper { // NOPMD private static final String LANDSCAPE_COLLECTION = "landscape"; private static final String REPLAY_COLLECTION = "replay"; - private MongoClient client; + @SuppressWarnings("PMD") + private static MongoClient client; private final String host; @@ -54,10 +55,10 @@ public MongoHelper(final String host, final String port, final String dbName) { this.port = port; this.dbName = dbName; - if (this.client == null) { - this.client = new MongoClient(new MongoClientURI(this.getUri())); + if (client == null) { + client = new MongoClient(new MongoClientURI(this.getUri())); } else { - throw new IllegalStateException("Onl y one instance allowed"); + throw new IllegalStateException("Only one instance allowed"); } } diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepository.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepository.java index e1184414..1dc6045f 100644 --- a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepository.java +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeJsonApiRepository.java @@ -25,14 +25,12 @@ * {@code explorviz.properties} resource. * *

- * * This repository will return all requested landscape objects in the json api format, which is the * format the objects are persisted in internally. Prefer this class over * {@link MongoLandscapeRepository} if you don't need an actually landscape object to avoid costy * de-/serialization. * *

- * */ public class MongoLandscapeJsonApiRepository implements LandscapeRepository { @@ -48,7 +46,7 @@ public class MongoLandscapeJsonApiRepository implements LandscapeRepository getByTimestamp(final long timestamp) { landscapeDocument.append(MongoHelper.FIELD_TIMESTAMP, timestamp); final FindIterable result = landscapeCollection.find(landscapeDocument); - - if (result.first() == null) { + final Document first = result.first(); + if (first == null) { return Optional.empty(); - } else { - return Optional.of((String) result.first().get(MongoHelper.FIELD_LANDSCAPE)); } + final Object landscape = first.get(MongoHelper.FIELD_LANDSCAPE); + if (landscape == null) { + return Optional.empty(); + } + return Optional.of((String) landscape); } @Override public Optional getByTimestamp(final Timestamp timestamp) { - return this.getByTimestamp(timestamp.getTimestamp()); + return this.getByTimestamp(timestamp.getUnixTimestamp()); } @Override @@ -117,13 +118,19 @@ public Optional getById(final String id) { final FindIterable result = landscapeCollection.find(landscapeDocument); - if (result.first() == null) { + final Document first = result.first(); + if (first == null) { return Optional.empty(); - } else { - return Optional.of((String) result.first().get(MongoHelper.FIELD_LANDSCAPE)); } + + final Object landscape = first.get(MongoHelper.FIELD_LANDSCAPE); + if (landscape == null) { + return Optional.empty(); + } + return Optional.of((String) landscape); } + @Override public void cleanup(final long from) { final long enddate = from - TimeUnit.MINUTES.toMillis(this.intervalInMinutes); @@ -162,12 +169,17 @@ public int getTotalRequests(final long timestamp) { final FindIterable result = landscapeCollection.find(landscapeDocument); - if (result.first() == null) { + final Document first = result.first(); + if (first == null) { throw new ClientErrorException("Landscape not found for provided timestamp " + timestamp, Response.Status.NOT_FOUND); - } else { - return (int) result.first().get(MongoHelper.FIELD_REQUESTS); } + + final Object requests = first.get(MongoHelper.FIELD_REQUESTS); + if (requests == null) { + return 0; + } + return (int) requests; } diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepository.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepository.java index 6897c75c..b6ecbe83 100644 --- a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepository.java +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoLandscapeRepository.java @@ -59,7 +59,7 @@ public Optional getByTimestamp(final long timestamp) { @Override public Optional getByTimestamp(final Timestamp timestamp) { - return this.getByTimestamp(timestamp.getTimestamp()); + return this.getByTimestamp(timestamp.getUnixTimestamp()); } @Override diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepository.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepository.java index 40c77f48..0db2db80 100644 --- a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepository.java +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayJsonApiRepository.java @@ -90,16 +90,21 @@ public Optional getByTimestamp(final long timestamp) { final FindIterable result = landscapeCollection.find(landscapeDocument); - if (result.first() == null) { + final Document first = result.first(); + if (first == null) { return Optional.empty(); - } else { - return Optional.of((String) result.first().get(MongoHelper.FIELD_LANDSCAPE)); } + + final Object landscape = first.get(MongoHelper.FIELD_LANDSCAPE); + if (landscape == null) { + return Optional.empty(); + } + return Optional.of((String) landscape); } @Override public Optional getByTimestamp(final Timestamp timestamp) { - return this.getByTimestamp(timestamp.getTimestamp()); + return this.getByTimestamp(timestamp.getUnixTimestamp()); } @Override @@ -111,11 +116,16 @@ public Optional getById(final String id) { final FindIterable result = landscapeCollection.find(landscapeDocument); - if (result.first() == null) { + final Document first = result.first(); + if (first == null) { return Optional.empty(); - } else { - return Optional.of((String) result.first().get(MongoHelper.FIELD_LANDSCAPE)); } + + final Object landscape = first.get(MongoHelper.FIELD_LANDSCAPE); + if (landscape == null) { + return Optional.empty(); + } + return Optional.of((String) landscape); } @Override @@ -127,13 +137,17 @@ public int getTotalRequestsByTimestamp(final long timestamp) { final FindIterable result = landscapeCollection.find(landscapeDocument); - if (result.first() == null) { - throw new ClientErrorException("Replay not found for provided timestamp " + timestamp, + final Document first = result.first(); + if (first == null) { + throw new ClientErrorException("Landscape not found for provided timestamp " + timestamp, Response.Status.NOT_FOUND); - } else { - return (int) result.first().get(MongoHelper.FIELD_REQUESTS); + } + final Object requests = first.get(MongoHelper.FIELD_REQUESTS); + if (requests == null) { + return 0; } + return (int) requests; } @Override diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepository.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepository.java index 583b6b23..fe8fe38c 100644 --- a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepository.java +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/MongoReplayRepository.java @@ -52,7 +52,7 @@ public Optional getByTimestamp(final long timestamp) { @Override public Optional getByTimestamp(final Timestamp timestamp) { - return this.getByTimestamp(timestamp.getTimestamp()); + return this.getByTimestamp(timestamp.getUnixTimestamp()); } @Override diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampQueryHelper.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampQueryHelper.java new file mode 100644 index 00000000..5f506506 --- /dev/null +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampQueryHelper.java @@ -0,0 +1,87 @@ +package net.explorviz.history.repository.persistence.mongo; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import net.explorviz.landscape.model.store.Timestamp; + +/** + * Contains helper methods to filter and sort collections of {@link Timestamp}s. + */ +public final class TimestampQueryHelper { + + private static final String MUST_BE_POSITIVE_INTEGER = "Timestamp must be positive integer"; + + private TimestampQueryHelper() { + // Utility class + } + + /** + * In-place filter the list of timestamps by a time range. Only timestamps within the given + * time range are preserved. + * + * @param timestamps the list of timestamps to filter + * @param from Lower limit, must be a UNIX epoch or null if no lower limit should be used + * @param to Upper limit, must be a UNIX epoch or null if not upper limit should be used + * @return the filtered list + */ + public static List filterByTimeRange(final List timestamps, + final String from, + final String to) { + List result = new ArrayList<>(timestamps); + if (from != null) { + final long fromTs = Long.parseLong(from); + if (fromTs < 0) { + throw new IllegalArgumentException(MUST_BE_POSITIVE_INTEGER); + } + result = timestamps.parallelStream() + .filter(t -> fromTs <= t.getUnixTimestamp()) + .collect(Collectors.toList()); + } + if (to != null) { + final long toTs = Long.parseLong(to); + if (toTs < 0) { + throw new IllegalArgumentException(MUST_BE_POSITIVE_INTEGER); + } + result = result.parallelStream() + .filter(t -> toTs >= t.getUnixTimestamp()) + .collect(Collectors.toList()); + } + return result; + } + + + /** + * Sorts the list of timestamp descending. + * + * @param timestamps timestamps to sort + */ + public static List sort(final List timestamps) { + final List result = new ArrayList<>(timestamps); + // Sort descending + result.sort((t1, t2) -> { + if (t1.getUnixTimestamp() == t2.getUnixTimestamp()) { + return 0; + } + return t1.getUnixTimestamp() > t2.getUnixTimestamp() ? -1 : 1; + }); + return result; + } + + + /** + * Paginates the collection of timestamp. + * + * @param timestamps list of timestamps to paginate + * @param number the page index + * @param size page size + * @return the page of the collection according to page number and size + */ + public static List paginate(final List timestamps, final int number, + final int size) { + final int pageFrom = Math.min(number * size, timestamps.size()); + final int pageTo = Math.min(size * number + size, timestamps.size()); + return new ArrayList<>(timestamps.subList(pageFrom, pageTo)); + } + +} diff --git a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampRepository.java b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampRepository.java index 05f7e3fd..eb1a2356 100644 --- a/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampRepository.java +++ b/history-service/src/main/java/net/explorviz/history/repository/persistence/mongo/TimestampRepository.java @@ -5,7 +5,7 @@ import com.mongodb.client.model.Projections; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; +import java.util.Locale; import javax.inject.Inject; import net.explorviz.landscape.model.store.Timestamp; import net.explorviz.shared.querying.Query; @@ -13,17 +13,15 @@ import net.explorviz.shared.querying.QueryResult; import net.explorviz.shared.querying.Queryable; import org.bson.Document; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Auxiliary repository for accessing timestamps of persistent landscapes objects. - * - * */ public class TimestampRepository implements Queryable { - private static final Logger LOGGER = LoggerFactory.getLogger(TimestampRepository.class); + private static final String FILTER_ARG_TYPE = "type"; + private static final String FILTER_ARG_FROM = "from"; + private static final String FILTER_ARG_TO = "to"; private final MongoHelper mongoHelper; @@ -36,84 +34,57 @@ public TimestampRepository(final MongoHelper helper) { @Override public QueryResult query(final Query query) throws QueryException { - final String filterArgType = "type"; - final String filterArgFrom = "from"; - final String filterArgTo = "to"; - List result = new ArrayList<>(); - if (query.getFilters().get(filterArgType) != null) { - for (final String type : query.getFilters().get(filterArgType)) { - if (type.toLowerCase().contentEquals("landscape")) { - result.addAll(this.getLandscapeTimestamps()); - } else if (type.toLowerCase().contentEquals("replay")) { - result.addAll(this.getReplayTimestamps()); - } else { - // Unknown type - return new QueryResult<>(query, new ArrayList(), 0); - } - } - } else { // Add all + if (query.getFilters().get(FILTER_ARG_TYPE) == null) { result.addAll(this.getReplayTimestamps()); result.addAll(this.getLandscapeTimestamps()); + } else { // Add all + findOfType(query.getFilters().get(FILTER_ARG_TYPE), result); } - if (query.getFilters().get(filterArgFrom) != null) { - if (query.getFilters().get(filterArgFrom).size() > 1) { - LOGGER.warn("More than one 'from' given, only applying the first"); - } - try { - final long fromTs = Long.parseLong(query.getFilters().get(filterArgFrom).get(0)); - if (fromTs <= 0) { - throw new QueryException("Filter 'from' must be positive"); - } - result = result.parallelStream() - .filter(t -> fromTs <= t.getTimestamp()) - .collect(Collectors.toList()); - } catch (final NumberFormatException e) { - throw new QueryException("Filter 'from' must be integral", e); - } + final List fromFilters = query.getFilters().get(FILTER_ARG_FROM); + final List toFilters = query.getFilters().get(FILTER_ARG_TO); + + + String from = null; + String to = null; + if (toFilters != null && !toFilters.isEmpty()) { + to = toFilters.get(0); + } + if (fromFilters != null && !fromFilters.isEmpty()) { + from = fromFilters.get(0); } - if (query.getFilters().get(filterArgTo) != null) { - if (query.getFilters().get(filterArgTo).size() > 1) { - LOGGER.warn("More than one 'to' given, only applying the first"); - } - try { - final long toTs = Long.parseLong(query.getFilters().get(filterArgTo).get(0)); - if (toTs <= 0) { - throw new QueryException("Filter 'to' must be positive"); - } - result = result.parallelStream() - .filter(t -> toTs >= t.getTimestamp()) - .collect(Collectors.toList()); - } catch (final NumberFormatException e) { - throw new QueryException("Filter 'from' must be integral", e); - } + try { + result = TimestampQueryHelper.filterByTimeRange(result, from, to); + } catch (final IllegalArgumentException e) { + throw new QueryException("Filters 'from' and 'to' must be integral values", e); } - // Sort descending - result.sort((t1, t2) -> { - if (t1.getTimestamp() == t2.getTimestamp()) { - return 0; - } - return t1.getTimestamp() > t2.getTimestamp() ? -1 : 1; - }); + + result = TimestampQueryHelper.sort(result); // Paginate final long total = result.size(); if (query.doPaginate()) { - final int pageFrom = Math.min(query.getPageNumber() * query.getPageSize(), result.size()); - final int pageTo = Math.min(query.getPageSize() * query.getPageNumber() + query.getPageSize(), - result.size()); - - result = new ArrayList<>(result.subList(pageFrom, pageTo)); + result = TimestampQueryHelper.paginate(result, query.getPageNumber(), query.getPageSize()); } return new QueryResult<>(query, result, total); } + private void findOfType(final List types, final List result) { + for (final String type : types) { + if (type.toLowerCase(Locale.ENGLISH).contentEquals("landscape")) { + result.addAll(this.getLandscapeTimestamps()); + } else if (type.toLowerCase(Locale.ENGLISH).contentEquals("replay")) { + result.addAll(this.getReplayTimestamps()); + } + } + } + /** diff --git a/history-service/src/main/java/net/explorviz/history/server/resources/HttpStatus.java b/history-service/src/main/java/net/explorviz/history/server/resources/HttpStatus.java new file mode 100644 index 00000000..cbe7934c --- /dev/null +++ b/history-service/src/main/java/net/explorviz/history/server/resources/HttpStatus.java @@ -0,0 +1,15 @@ +package net.explorviz.history.server.resources; + +/** + * Utility class containing HTTP status code constants. + */ +public final class HttpStatus { + + public static final String NOT_FOUND = "404"; + public static final String OK = "200"; + + private HttpStatus(){} + + + +} diff --git a/history-service/src/main/java/net/explorviz/history/server/resources/LandscapeResource.java b/history-service/src/main/java/net/explorviz/history/server/resources/LandscapeResource.java index d7c67385..9998fc3e 100644 --- a/history-service/src/main/java/net/explorviz/history/server/resources/LandscapeResource.java +++ b/history-service/src/main/java/net/explorviz/history/server/resources/LandscapeResource.java @@ -27,7 +27,7 @@ import net.explorviz.history.repository.persistence.LandscapeRepository; import net.explorviz.history.repository.persistence.ReplayRepository; import net.explorviz.history.repository.persistence.mongo.LandscapeSerializationHelper; -import net.explorviz.history.util.ResourceHelper; +import net.explorviz.history.util.UploadHelper; import net.explorviz.landscape.model.landscape.Landscape; import net.explorviz.security.user.Role; import net.explorviz.shared.security.filters.Secure; @@ -44,7 +44,7 @@ @RolesAllowed({Role.ADMIN_NAME, Role.USER_NAME}) @Tag(name = "Landscapes") @SecurityScheme(type = SecuritySchemeType.HTTP, name = "token", scheme = "bearer", - bearerFormat = "JWT") + bearerFormat = "JWT") @SecurityRequirement(name = "token") @Secure public class LandscapeResource { @@ -54,16 +54,25 @@ public class LandscapeResource { private static final String MEDIA_TYPE = "application/vnd.api+json"; private static final long QUERY_PARAM_DEFAULT_VALUE_LONG = 0L; private static final String QUERY_PARAM_EMPTY_STRING = ""; + private static final String TIMESTAMP_IS_MANDATORY = "Query parameter 'timestamp' is mandatory"; + private final LandscapeRepository landscapeStringRepo; private final ReplayRepository replayStringRepo; private final LandscapeSerializationHelper serializationHelper; + /** + * Creates a new resource. Handled by DI. + * + * @param landscapeStringRepo the repository for {@link Landscape}s + * @param replayStringRepo the repository for Replay-{@link Landscape}s + * @param serializationHelper helper to serialize landscapes to JSON:API + */ @Inject public LandscapeResource(final LandscapeRepository landscapeStringRepo, - final ReplayRepository replayStringRepo, - final LandscapeSerializationHelper serializationHelper) { + final ReplayRepository replayStringRepo, + final LandscapeSerializationHelper serializationHelper) { this.landscapeStringRepo = landscapeStringRepo; this.replayStringRepo = replayStringRepo; this.serializationHelper = serializationHelper; @@ -82,11 +91,12 @@ public LandscapeResource(final LandscapeRepository landscapeStringRepo, @Path("{id}") @Produces(MEDIA_TYPE) @Operation(summary = "Find a landscape by its id") - @ApiResponse(responseCode = "200", description = "Response contains the requested landscape.", - content = @Content(schema = @Schema(implementation = Landscape.class))) - @ApiResponse(responseCode = "404", description = "No landscape with such id.") + @ApiResponse(responseCode = HttpStatus.OK, + description = "Response contains the requested landscape.", + content = @Content(schema = @Schema(implementation = Landscape.class))) + @ApiResponse(responseCode = HttpStatus.NOT_FOUND, description = "No landscape with such id.") public String getLandscapeById(@Parameter(description = "Id of the landscape", - required = true) @PathParam("id") final String id) { + required = true) @PathParam("id") final String id) { // Check existence in landscapeRepo and replayRepo or throw Exception // this can be done better since Java 9 @@ -94,7 +104,8 @@ public String getLandscapeById(@Parameter(description = "Id of the landscape", .filter(Optional::isPresent) .map(Optional::get) .findFirst() - .orElseThrow(() -> new NotFoundException("Landscape with id " + id + " not found.")); // NOCS + .orElseThrow( + () -> new NotFoundException("Landscape with id " + id + " not found.")); // NOCS } /** @@ -106,15 +117,17 @@ public String getLandscapeById(@Parameter(description = "Id of the landscape", @GET @Produces(MEDIA_TYPE) @Operation(summary = "Find a landscape by its timestamp") - @ApiResponse(responseCode = "200", - description = "Response contains the first landscape with the given timestamp.", - content = @Content(schema = @Schema(implementation = Landscape.class))) - @ApiResponse(responseCode = "404", description = "No landscape with the given timestamp.") + @ApiResponse(responseCode = HttpStatus.OK, + description = "Response contains the first landscape with the given timestamp.", + content = @Content(schema = @Schema(implementation = Landscape.class))) + @ApiResponse(responseCode = HttpStatus.NOT_FOUND, + description = "No landscape with the given timestamp.") public String getLandscape(@Parameter(description = "The timestamp to filter by.", - required = true) @QueryParam("timestamp") final long timestamp) { + required = true) @QueryParam("timestamp") + final long timestamp) { if (timestamp == QUERY_PARAM_DEFAULT_VALUE_LONG) { - throw new BadRequestException("Query parameter 'timestamp' is mandatory"); + throw new BadRequestException(TIMESTAMP_IS_MANDATORY); } // Check existence in landscapeRepo and replayRepo or throw Exception @@ -130,25 +143,26 @@ public String getLandscape(@Parameter(description = "The timestamp to filter by. } /** - * Provides a json file for downloading a landscape from the frontend + * Provides a json file for downloading a landscape from the frontend. * * @param timestamp of the {@link Landscape} to return * @return the {@link Landscape} as a json file with the given timestamp - * */ @GET @Path("/download") @Produces("application/json") @Operation(summary = "Download a landscape by its timestamp") - @ApiResponse(responseCode = "200", - description = "Response contains the first landscape with the given timestamp.", - content = @Content(schema = @Schema(implementation = Landscape.class))) - @ApiResponse(responseCode = "404", description = "No landscape with the given timestamp.") + @ApiResponse(responseCode = HttpStatus.OK, + description = "Response contains the first landscape with the given timestamp.", + content = @Content(schema = @Schema(implementation = Landscape.class))) + @ApiResponse(responseCode = HttpStatus.NOT_FOUND, + description = "No landscape with the given timestamp.") public String downloadLandscape(@Parameter(description = "The timestamp to filter by.", - required = true) @QueryParam("timestamp") final long timestamp) { + required = true) @QueryParam("timestamp") + final long timestamp) { if (timestamp == QUERY_PARAM_DEFAULT_VALUE_LONG) { - throw new BadRequestException("Query parameter 'timestamp' is mandatory"); + throw new BadRequestException(TIMESTAMP_IS_MANDATORY); } // Check existence in landscapeRepo and replayRepo or throw Exception @@ -165,36 +179,41 @@ public String downloadLandscape(@Parameter(description = "The timestamp to filte } /** - * Accepts uploading a landscape from the frontend + * Accepts uploading a landscape from the frontend. * * @param uploadedInputStream json-file of the uploaded landscape - * @param fileInfo information of the file, e.g., the filename + * @param fileInfo information of the file, e.g., the filename */ @POST @Path("/replay/upload") @Consumes("multipart/form-data") @Produces(MEDIA_TYPE) @Operation(summary = "Upload a landscape file from the frontend") - @ApiResponse(responseCode = "200", description = "Response contains the uploaded landscape file.", - content = @Content(schema = @Schema(implementation = Landscape.class))) - @ApiResponse(responseCode = "404", description = "Landscape file could not be uploaded.") + @ApiResponse(responseCode = HttpStatus.OK, + description = "Response contains the uploaded landscape file.", + content = @Content(schema = @Schema(implementation = Landscape.class))) + @ApiResponse(responseCode = HttpStatus.NOT_FOUND, + description = "Landscape file could not be uploaded.") public String uploadLandscape( @Parameter(description = "The name of the file.", - required = true) @QueryParam("filename") final String fileName, + required = true) @QueryParam("filename") final String fileName, @Parameter(description = "The uploaded landscape file.", - required = true) @FormDataParam("file") final InputStream uploadedInputStream, + required = true) @FormDataParam("file") final InputStream uploadedInputStream, @Parameter(description = "The file information of the uploaded landscape.", - required = true) @FormDataParam("file") final FormDataContentDisposition fileInfo) { + required = true) @FormDataParam("file") + final FormDataContentDisposition fileInfo) { // TODO check for empty uploaded landscape file - if (fileName == QUERY_PARAM_EMPTY_STRING) { + if (fileName.equals(QUERY_PARAM_EMPTY_STRING)) { throw new BadRequestException("Query parameter 'filename' is mandatory"); } - LOGGER.info("Uploaded Filename: " + fileName); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Uploaded Filename: " + fileName); + } // split the passed filename - final String fileNameWithoutExtension = ResourceHelper.removeFileNameExtension(fileName); + final String fileNameWithoutExtension = UploadHelper.removeFileNameExtension(fileName); final String[] splittedFilename = fileNameWithoutExtension.split("-"); final long parsedTimestamp = Long.valueOf(splittedFilename[0]); @@ -210,11 +229,11 @@ public String uploadLandscape( Landscape parsedLandscape = null; final String convertedInputStream = - ResourceHelper.convertInputstreamToString(uploadedInputStream); + UploadHelper.convertInputstreamToString(uploadedInputStream); try { parsedLandscape = this.serializationHelper.deserialize(convertedInputStream); - this.replayStringRepo.save(parsedLandscape.getTimestamp().getTimestamp(), + this.replayStringRepo.save(parsedLandscape.getTimestamp().getUnixTimestamp(), parsedLandscape, parsedLandscape.getTimestamp().getTotalRequests()); } catch (final DocumentSerializationException e) { diff --git a/history-service/src/main/java/net/explorviz/history/util/ResourceHelper.java b/history-service/src/main/java/net/explorviz/history/util/UploadHelper.java similarity index 64% rename from history-service/src/main/java/net/explorviz/history/util/ResourceHelper.java rename to history-service/src/main/java/net/explorviz/history/util/UploadHelper.java index e4f1e7fd..ebfff703 100644 --- a/history-service/src/main/java/net/explorviz/history/util/ResourceHelper.java +++ b/history-service/src/main/java/net/explorviz/history/util/UploadHelper.java @@ -9,18 +9,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ResourceHelper { +/** + * Utility class that contains helper methods for uploading landscapes. + */ +public final class UploadHelper { - private static final Logger LOGGER = LoggerFactory.getLogger(ResourceHelper.class); + private static final Logger LOGGER = LoggerFactory.getLogger(UploadHelper.class); + private static final int BUFFER_SIZE = 1024; + + private UploadHelper() { + //Utility Class + } /** - * Remove the extension of a passed filename + * Remove the extension of a passed filename. * * @param fileName the fileName * @return the trimmed fileName */ public static String removeFileNameExtension(final String fileName) { - final int extPos = fileName.lastIndexOf("."); + final int extPos = fileName.lastIndexOf('.'); if (extPos == -1) { return fileName; } else { @@ -29,7 +37,7 @@ public static String removeFileNameExtension(final String fileName) { } /** - * Converts an InputStream to a string for further processing + * Converts an InputStream to a string for further processing. * * @param is the inputStream * @return the resulted string @@ -37,23 +45,22 @@ public static String removeFileNameExtension(final String fileName) { public static String convertInputstreamToString(final InputStream is) { byte[] inputByteArray; + try { inputByteArray = IOUtils.toByteArray(is); final InputStream inputStream = new ByteArrayInputStream(inputByteArray); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int nRead; - final byte[] data = new byte[1024]; - while ((nRead = inputStream.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); + int bytesRead; + final byte[] data = new byte[BUFFER_SIZE]; + while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, bytesRead); } buffer.flush(); final byte[] outputByteArray = buffer.toByteArray(); - final String uploadedInputStreamString = new String(outputByteArray, StandardCharsets.UTF_8); - - return uploadedInputStreamString; + return new String(outputByteArray, StandardCharsets.UTF_8); } catch (final IOException e) { LOGGER.error( diff --git a/history-service/src/test/java/net/explorviz/history/helper/DummyLandscapeHelper.java b/history-service/src/test/java/net/explorviz/history/helper/DummyLandscapeHelper.java index 35b9c758..21ba043e 100644 --- a/history-service/src/test/java/net/explorviz/history/helper/DummyLandscapeHelper.java +++ b/history-service/src/test/java/net/explorviz/history/helper/DummyLandscapeHelper.java @@ -1,5 +1,5 @@ package net.explorviz.history.helper; - +// CHECKSTYLE:OFF import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; @@ -27,6 +27,8 @@ private DummyLandscapeHelper() { // Utility Class } + + /** * Returns a random number between minValue and maxValue. * @@ -52,9 +54,9 @@ public static int getNextSequenceId() { } /** - * Creates a new system + * Creates a new system. * - * @param name - name of the system + * @param name - name of the system * @param parentLandscape - parent landscape * @return created system */ @@ -72,9 +74,9 @@ public static System createSystem(final String name, final Landscape parentLands } /** - * Creates a new nodeGroup + * Creates a new nodeGroup. * - * @param name - name of the nodeGroup + * @param name - name of the nodeGroup * @param system - parent system * @return created nodeGroup */ @@ -86,22 +88,22 @@ public static NodeGroup createNodeGroup(final String name, final System system) } /** - * Creates a new node + * Creates a new node. * - * @param ipAddress - ipAddress of the node + * @param ipAddress - ipAddress of the node * @param parentNodeGroup - parent nodeGroup * @return created node */ public static Node createNode(final String ipAddress, final NodeGroup parentNodeGroup, - final Landscape landscape) { + final Landscape landscape) { final Node node = new Node(idGen.generateId()); node.setIpAddress(ipAddress); node.setParent(parentNodeGroup); // set random usage node.setCpuUtilization((double) getRandomNum(10, 100) / 100); - node.setFreeRAM((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); - node.setUsedRAM((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); + node.setFreeRam((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); + node.setUsedRam((long) getRandomNum(1, 4) * LandscapeDummyCreator.formatFactor); // create a new node event landscape.createNewEvent(idGen.generateId(), @@ -113,15 +115,15 @@ public static Node createNode(final String ipAddress, final NodeGroup parentNode } /** - * Creates a new application + * Creates a new application. * - * @param name - name of the application + * @param name - name of the application * @param parentNode - name of the parent node - * @param landscape - name of the landscape + * @param landscape - name of the landscape * @return created application */ public static Application createApplication(final String name, final Node parentNode, - final Landscape landscape) { + final Landscape landscape) { final Application application = new Application(idGen.generateId()); LandscapeDummyCreator.applicationId = LandscapeDummyCreator.applicationId + 1; @@ -147,16 +149,18 @@ public static Application createApplication(final String name, final Node parent } /** - * Create communication between applications + * Create communication between applications. * - * @param source - sourceApplication - * @param target - targetApplication + * @param source - sourceApplication + * @param target - targetApplication * @param landscape - parent landscape - * @param requests - number of requests + * @param requests - number of requests * @return created ApplicationCommunication */ public static ApplicationCommunication createApplicationCommunication(final Application source, - final Application target, final Landscape landscape, final int requests) { + final Application target, + final Landscape landscape, + final int requests) { final ApplicationCommunication communication = new ApplicationCommunication(idGen.generateId()); communication.setSourceApplication(source); communication.setTargetApplication(target); @@ -172,15 +176,15 @@ public static ApplicationCommunication createApplicationCommunication(final Appl } /** - * Creates a component + * Creates a component. * - * @param name - name of the component + * @param name - name of the component * @param parent - parent component - * @param app - parent application + * @param app - parent application * @return created component */ public static Component createComponent(final String name, final Component parent, - final Application app) { + final Application app) { final Component component = new Component(idGen.generateId()); component.setName(name); component.setParentComponent(parent); @@ -195,15 +199,15 @@ public static Component createComponent(final String name, final Component paren } /** - * Creates a clazz + * Creates a clazz. * - * @param name - name of the clazz - * @param component - parent component + * @param name - name of the clazz + * @param component - parent component * @param instanceCount - number of instances * @return created clazz */ public static Clazz createClazz(final String name, final Component component, - final int instanceCount) { + final int instanceCount) { final Clazz clazz = new Clazz(idGen.generateId()); clazz.setName(name); clazz.setFullQualifiedName(component.getFullQualifiedName() + "." + name); @@ -215,17 +219,18 @@ public static Clazz createClazz(final String name, final Component component, } /** - * Creating a communication between two clazzes within the dummy landscape + * Creating a communication between two clazzes within the dummy landscape. * - * @param traceId - id of the trace - * @param requests - number of requests + * @param traceId - id of the trace + * @param requests - number of requests * @param sourceClazz - sourceClazz * @param targetClazz - targetClazz * @param application - parent application */ public static void createClazzCommunication(final String traceId, final int tracePosition, - final int requests, final Clazz sourceClazz, final Clazz targetClazz, - final Application application) { + final int requests, final Clazz sourceClazz, + final Clazz targetClazz, + final Application application) { final float averageResponseTime = 0L + getRandomNum(10, 1000); final float overallTraceDuration = 0L + getRandomNum(1000, 10000); diff --git a/history-service/src/test/java/net/explorviz/history/helper/LandscapeDummyCreator.java b/history-service/src/test/java/net/explorviz/history/helper/LandscapeDummyCreator.java index 5f563099..b1ae2af0 100644 --- a/history-service/src/test/java/net/explorviz/history/helper/LandscapeDummyCreator.java +++ b/history-service/src/test/java/net/explorviz/history/helper/LandscapeDummyCreator.java @@ -20,10 +20,10 @@ public final class LandscapeDummyCreator { // CHECKSTYLE.OFF: MultipleStringLiteralsCheck - Much more readable than NOCS in many lines // CHECKSTYLE.OFF: MagicNumberCheck - Much more readable than NOCS in many lines - public static int applicationId = 0; - public static int formatFactor = 1024 * 1024 * 1024; + public static int applicationId = 0; // NOCS + public static int formatFactor = 1024 * 1024 * 1024; // NOCS - private static IdGenerator idGen; + public static IdGenerator idGen; // NOCS private LandscapeDummyCreator() { @@ -32,7 +32,7 @@ private LandscapeDummyCreator() { /** - * Create a dummy landscape for demo and mocking purposes + * Create a dummy landscape for demo and mocking purposes. * * @return a prepared dummy landscape */ @@ -314,17 +314,10 @@ public static Landscape createDummyLandscape(final IdGenerator idGen) { } /** - * <<<<<<< HEAD Creates a dummy webshop application within the dummy landscape + * Creats a webshop application. * - * @param application - * @return webshop application ======= Creating a communication between two clazzes within the - * dummy landscape. - * - * @param traceId the id of the trace - * @param requests the request - * @param sourceClazz the source class - * @param targetClazz the target class - * @param application the appliaction >>>>>>> dev-1 + * @param application the appliaction + * @return webshop application */ private static Application createWebshopApplication(final Application application) { @@ -456,9 +449,9 @@ private static Application createWebshopApplication(final Application applicatio } /** - * Creates a dummy database-query including application within the dummy landscape + * Creates a dummy database-query including application within the dummy landscape. * - * @param application + * @param application the application to create the connector for * @return database connector application */ private static Application createDatabaseConnector(final Application application) { diff --git a/history-service/src/test/java/net/explorviz/history/server/resources/LandscapeResourceTest.java b/history-service/src/test/java/net/explorviz/history/server/resources/LandscapeResourceTest.java index 72fa688f..1e17d64e 100644 --- a/history-service/src/test/java/net/explorviz/history/server/resources/LandscapeResourceTest.java +++ b/history-service/src/test/java/net/explorviz/history/server/resources/LandscapeResourceTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; + import com.github.jasminb.jsonapi.ResourceConverter; import com.github.jasminb.jsonapi.SerializationFeature; import com.github.jasminb.jsonapi.exceptions.DocumentSerializationException; @@ -44,6 +45,11 @@ public class LandscapeResourceTest { @Mock(lenient = true) private ReplayRepository replayStringRepo; + /** + * Initializes dummy landscapes test against and the resource converter. + * + * @throws DocumentSerializationException on resource converter fails + */ @BeforeEach public void setUp() throws DocumentSerializationException { diff --git a/history-service/src/test/java/net/explorviz/history/server/resources/TimestampResourceTest.java b/history-service/src/test/java/net/explorviz/history/server/resources/TimestampResourceTest.java index df20a78f..5dce4b49 100644 --- a/history-service/src/test/java/net/explorviz/history/server/resources/TimestampResourceTest.java +++ b/history-service/src/test/java/net/explorviz/history/server/resources/TimestampResourceTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -26,11 +27,18 @@ * requests. */ @ExtendWith(MockitoExtension.class) +@SuppressWarnings("MagicNumber") public class TimestampResourceTest { + private static final String FILTER_TYPE = "filter[type]"; + private static final String TYPE_LANDSCAPE = "landscape"; + private static final String TYPE_REPLAY = "replay"; + private static final String PAGE_NUMBER = "page[number]"; + private static final String PAGE_SIZE = "page[size]"; + private static final String FILTER_FROM = "filter[from]"; private TimestampResource timestampResource; - + // CHECKSTYLE.OFF: MultipleStringLiteralsCheck - Much more readable than NOCS in many lines @Mock(lenient = true) private TimestampRepository timestampRepo; @@ -38,6 +46,11 @@ public class TimestampResourceTest { private List serviceGeneratedTimestamps; private List userUploadedTimestamps; + /** + * Creates a bunch of timestamp to run the tests against and initializes the mocks. + * + * @throws QueryException Ignore, mocking + */ @BeforeEach public void setUp() throws QueryException { @@ -70,7 +83,7 @@ public void setUp() throws QueryException { @DisplayName("Return all service-generated timestamps.") public void giveAllServiceGenerated() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[type]", "landscape"); + params.add(FILTER_TYPE, TYPE_LANDSCAPE); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); assertEquals(this.reverse(this.serviceGeneratedTimestamps), @@ -82,7 +95,7 @@ public void giveAllServiceGenerated() { @DisplayName("Return all user-uploaded timestamps.") public void giveAllUserUploadedOnlyFlag() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[type]", "replay"); + params.add(FILTER_TYPE, TYPE_REPLAY); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); assertEquals(this.reverse(this.userUploadedTimestamps), @@ -94,9 +107,9 @@ public void giveAllUserUploadedOnlyFlag() { @DisplayName("Return first two service-generated timestamps.") public void giveServiceGeneratedBasedOnMaxLength() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("page[number]", "2"); - params.add("page[size]", "2"); - params.add("filter[type]", "landscape"); + params.add(PAGE_NUMBER, "2"); + params.add(PAGE_SIZE, "2"); + params.add(FILTER_TYPE, TYPE_LANDSCAPE); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); final List resultList = @@ -114,9 +127,9 @@ public void giveServiceGeneratedBasedOnMaxLength() { public void giveUserUploadedOnlyFlagBasedOnMaxLength() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("page[number]", "2"); - params.add("page[size]", "2"); - params.add("filter[type]", "replay"); + params.add(PAGE_NUMBER, "2"); + params.add(PAGE_SIZE, "2"); + params.add(FILTER_TYPE, TYPE_REPLAY); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); final List resultList = @@ -136,8 +149,8 @@ public void giveUserUploadedOnlyFlagBasedOnMaxLength() { public void giveAllServiceTimestampsAfterPassedTimestamp() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[from]", "1556302820"); - params.add("filter[type]", "landscape"); + params.add(FILTER_FROM, "1556302820"); + params.add(FILTER_TYPE, TYPE_LANDSCAPE); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -161,8 +174,8 @@ public void giveAllUserUploadedAllParams() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[from]", "1556302880"); - params.add("filter[type]", "replay"); + params.add(FILTER_FROM, "1556302880"); + params.add(FILTER_TYPE, TYPE_REPLAY); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -185,11 +198,11 @@ public void giveAllUserUploadedAllParams() { public void giveConcreteIntervalOfUserUploadedTimestamps() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[from]", "1556302880"); + params.add(FILTER_FROM, "1556302880"); params.add("filter[to]", "1556302900"); - params.add("filter[type]", "replay"); - params.add("page[size]", "3"); - params.add("page[number]", "0"); + params.add(FILTER_TYPE, TYPE_REPLAY); + params.add(PAGE_SIZE, "3"); + params.add(PAGE_NUMBER, "0"); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -211,8 +224,8 @@ public void giveConcreteIntervalOfUserUploadedTimestamps() { public void giveRemainingIntervalOfUserUploadedTimestamps() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[from]", "1556302880"); - params.add("filter[type]", "replay"); + params.add(FILTER_FROM, "1556302880"); + params.add(FILTER_TYPE, TYPE_REPLAY); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -236,8 +249,8 @@ public void giveRemainingIntervalOfUserUploadedTimestamps() { @DisplayName("Return concrete interval of service-generated timestamps.") public void giveConcreteIntervalOfServiceGeneratedTimestamps() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[from]", "1556302810"); - params.add("filter[type]", "landscape"); + params.add(FILTER_FROM, "1556302810"); + params.add(FILTER_TYPE, TYPE_LANDSCAPE); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -263,10 +276,10 @@ public void giveConcreteIntervalOfServiceGeneratedTimestamps() { public void giveRemainingIntervalOfServiceGeneratedTimestamps() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[from]", "1556302809"); - params.add("filter[type]", "landscape"); - params.add("page[number]", "0"); - params.add("page[size]", "5"); + params.add(FILTER_FROM, "1556302809"); + params.add(FILTER_TYPE, TYPE_LANDSCAPE); + params.add(PAGE_NUMBER, "0"); + params.add(PAGE_SIZE, "5"); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -286,13 +299,16 @@ public void giveRemainingIntervalOfServiceGeneratedTimestamps() { } @Test - @DisplayName("Return interval of user-uploaded timestamps starting at newest, when no timestamp was passed.") // NOCS + @DisplayName( + "Return interval of user-uploaded timestamps starting at newest, " + + "when no timestamp was passed.") + // NOCS public void giveMaxLengthIntervalOfUserPloadedTimestamps() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[type]", "replay"); - params.add("page[number]", "0"); - params.add("page[size]", "2"); + params.add(FILTER_TYPE, TYPE_REPLAY); + params.add(PAGE_NUMBER, "0"); + params.add(PAGE_SIZE, "2"); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); @@ -311,13 +327,15 @@ public void giveMaxLengthIntervalOfUserPloadedTimestamps() { } @Test - @DisplayName("Return interval of service-generated timestamps starting at newest, when no timestamp was passed.") // NOCS + @DisplayName("Return interval of service-generated timestamps starting at newest," + + " when no timestamp was passed.") + // NOCS public void giveMaxLengthIntervalOfServiceGeneratedTimestamps() { final MultivaluedHashMap params = new MultivaluedHashMap<>(); - params.add("filter[type]", "landscape"); - params.add("page[number]", "0"); - params.add("page[size]", "2"); + params.add(FILTER_TYPE, TYPE_LANDSCAPE); + params.add(PAGE_NUMBER, "0"); + params.add(PAGE_SIZE, "2"); final UriInfo ui = Mockito.mock(UriInfo.class); when(ui.getQueryParameters(true)).thenReturn(params); diff --git a/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/LandscapeResourceEndpointTest.java b/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/LandscapeResourceEndpointTest.java index 5575ff65..0fc644cc 100644 --- a/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/LandscapeResourceEndpointTest.java +++ b/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/LandscapeResourceEndpointTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; + import com.github.jasminb.jsonapi.ResourceConverter; import com.github.jasminb.jsonapi.SerializationFeature; import com.github.jasminb.jsonapi.exceptions.DocumentSerializationException; @@ -79,7 +80,7 @@ protected Application configure() { when(this.landscapeStringRepo.getById(this.currentLandscapeId)) .thenReturn(Optional.of(this.currentLandscape)); - when(this.landscapeStringRepo.getByTimestamp(this.currentLandscapeTimestamp.getTimestamp())) + when(this.landscapeStringRepo.getByTimestamp(this.currentLandscapeTimestamp.getUnixTimestamp())) .thenReturn(Optional.of(this.currentLandscape)); when(this.landscapeStringRepo.getById("2L")) .thenThrow(new NotFoundException("Landscape not found for provided 2L.")); @@ -100,7 +101,7 @@ public void checkOkStatusCodes() { // NOPMD response = this.target() .path(BASE_URL) - .queryParam(QUERY_PARAM_TIMESTAMP, this.currentLandscapeTimestamp.getTimestamp()) + .queryParam(QUERY_PARAM_TIMESTAMP, this.currentLandscapeTimestamp.getUnixTimestamp()) .request() .accept(MEDIA_TYPE) .get(); @@ -177,7 +178,7 @@ public void checkMediaTypeOfValidRequestAndResponseWithAcceptHeader() { public void checkQueryEndpointSuccess() { final Response response = this.target() .path(BASE_URL) - .queryParam(QUERY_PARAM_TIMESTAMP, this.currentLandscapeTimestamp.getTimestamp()) + .queryParam(QUERY_PARAM_TIMESTAMP, this.currentLandscapeTimestamp.getUnixTimestamp()) .request() .accept(MEDIA_TYPE) .get(); diff --git a/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/TimestampResourceEndpointTest.java b/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/TimestampResourceEndpointTest.java index 06d568b1..609967f5 100644 --- a/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/TimestampResourceEndpointTest.java +++ b/history-service/src/test/java/net/explorviz/history/server/resources/endpoints/TimestampResourceEndpointTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; + import java.util.ArrayList; import java.util.List; import javax.ws.rs.core.Application; @@ -31,7 +32,7 @@ public class TimestampResourceEndpointTest extends JerseyTest { private static final String BASE_URL = "v1/timestamps"; private static final String GENERIC_STATUS_ERROR_MESSAGE = "Wrong HTTP Status code."; - private static final String GENERIC_MEDIA_TYPE_ERROR_MESSAGE = "Wrong media type."; + private static final String MEDIA_TYPE_ERROR = "Wrong media type."; private static final String FILTER_FROM = "filter[from]"; private static final String FILTER_TYPE = "filter[type]"; @@ -41,36 +42,33 @@ public class TimestampResourceEndpointTest extends JerseyTest { private TimestampRepository timestampRepo; - private List serviceGeneratedTimestamps; - private List userUploadedTimestamps; - @Override protected Application configure() { // Called for each test // CHECKSTYLE.OFF: MagicNumber - this.serviceGeneratedTimestamps = new ArrayList<>(); - this.serviceGeneratedTimestamps.add(new Timestamp("1", 1_556_302_800L, 300)); - this.serviceGeneratedTimestamps.add(new Timestamp("2", 1_556_302_810L, 400)); - this.serviceGeneratedTimestamps.add(new Timestamp("3", 1_556_302_820L, 500)); - this.serviceGeneratedTimestamps.add(new Timestamp("4", 1_556_302_830L, 600)); - this.serviceGeneratedTimestamps.add(new Timestamp("5", 1_556_302_840L, 700)); - this.serviceGeneratedTimestamps.add(new Timestamp("6", 1_556_302_850L, 800)); - - this.userUploadedTimestamps = new ArrayList<>(); - this.userUploadedTimestamps.add(new Timestamp("7", 1_556_302_860L, 600)); - this.userUploadedTimestamps.add(new Timestamp("8", 1_556_302_870L, 700)); - this.userUploadedTimestamps.add(new Timestamp("9", 1_556_302_880L, 800)); - this.userUploadedTimestamps.add(new Timestamp("10", 1_556_302_890L, 900)); - this.userUploadedTimestamps.add(new Timestamp("11", 1_556_302_900L, 1000)); - this.userUploadedTimestamps.add(new Timestamp("12", 1_556_302_910L, 1100)); + final List serviceGeneratedTimestamps = new ArrayList<>(); + serviceGeneratedTimestamps.add(new Timestamp("1", 1_556_302_800L, 300)); + serviceGeneratedTimestamps.add(new Timestamp("2", 1_556_302_810L, 400)); + serviceGeneratedTimestamps.add(new Timestamp("3", 1_556_302_820L, 500)); + serviceGeneratedTimestamps.add(new Timestamp("4", 1_556_302_830L, 600)); + serviceGeneratedTimestamps.add(new Timestamp("5", 1_556_302_840L, 700)); + serviceGeneratedTimestamps.add(new Timestamp("6", 1_556_302_850L, 800)); + + final List userUploadedTimestamps = new ArrayList<>(); + userUploadedTimestamps.add(new Timestamp("7", 1_556_302_860L, 600)); + userUploadedTimestamps.add(new Timestamp("8", 1_556_302_870L, 700)); + userUploadedTimestamps.add(new Timestamp("9", 1_556_302_880L, 800)); + userUploadedTimestamps.add(new Timestamp("10", 1_556_302_890L, 900)); + userUploadedTimestamps.add(new Timestamp("11", 1_556_302_900L, 1000)); + userUploadedTimestamps.add(new Timestamp("12", 1_556_302_910L, 1100)); // CHECKSTYLE.ON: MagicNumber this.timestampRepo = Mockito.mock(TimestampRepository.class); - when(this.timestampRepo.getLandscapeTimestamps()).thenReturn(this.serviceGeneratedTimestamps); - when(this.timestampRepo.getReplayTimestamps()).thenReturn(this.userUploadedTimestamps); + when(this.timestampRepo.getLandscapeTimestamps()).thenReturn(serviceGeneratedTimestamps); + when(this.timestampRepo.getReplayTimestamps()).thenReturn(userUploadedTimestamps); return new ResourceConfig().register(new TimestampResource(this.timestampRepo)); } @@ -132,7 +130,7 @@ public void checkBadRequestStatusCodes() throws QueryException { // NOPMD public void checkNotAcceptableMediaTypeStatusCode() { final Response response = this.target().path(BASE_URL).request().accept(MediaType.TEXT_PLAIN).get(); - assertEquals(GENERIC_MEDIA_TYPE_ERROR_MESSAGE, + assertEquals(MEDIA_TYPE_ERROR, Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus()); } @@ -144,14 +142,14 @@ public void checkMediaTypeOfValidRequestAndResponse() throws QueryException { .thenReturn(new QueryResult(null, null, -1)); final Response response = this.target().path(BASE_URL).request().get(); - assertEquals(GENERIC_MEDIA_TYPE_ERROR_MESSAGE, MEDIA_TYPE, response.getMediaType().toString()); + assertEquals(MEDIA_TYPE_ERROR, MEDIA_TYPE, response.getMediaType().toString()); } @Test public void checkMediaTypeOfValidRequestAndResponseWithAcceptHeader() throws QueryException { when(this.timestampRepo.query(ArgumentMatchers.any())).thenCallRealMethod(); final Response response = this.target().path(BASE_URL).request().accept(MEDIA_TYPE).get(); - assertEquals(GENERIC_MEDIA_TYPE_ERROR_MESSAGE, MEDIA_TYPE, response.getMediaType().toString()); + assertEquals(MEDIA_TYPE_ERROR, MEDIA_TYPE, response.getMediaType().toString()); } // TODO test for valid response and JSON-API conformity diff --git a/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/configuration/GenericExplorVizExternalLogAdapter.java b/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/configuration/GenericExplorVizExternalLogAdapter.java index c0aeb409..6e3a3020 100644 --- a/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/configuration/GenericExplorVizExternalLogAdapter.java +++ b/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/configuration/GenericExplorVizExternalLogAdapter.java @@ -56,10 +56,10 @@ public static void sendBeforeRecord(final long timestamp, final long traceId, interfaceImpl); } - private static void sendBeforeGeneric(final byte ID, final long timestamp, final long traceId, + private static void sendBeforeGeneric(final byte id, final long timestamp, final long traceId, final int orderIndex, final int objectId, final String operationSignature, final String clazz, final String implementedInterface) { - EXPLORVIZ_BUFFER.put(ID); + EXPLORVIZ_BUFFER.put(id); EXPLORVIZ_BUFFER.putLong(traceId); EXPLORVIZ_BUFFER.putInt(orderIndex); EXPLORVIZ_BUFFER.putInt(objectId); @@ -77,7 +77,7 @@ private static void sendBufferIfHasElements(final long timestamp) { if (firstTimestamp == -1) { firstTimestamp = timestamp; // firstWallclockTimestamp = System.nanoTime(); - } else { + } else { // NOPMD // final long passedTime = timestamp - firstTimestamp; // while (REPLAY_IN_REALTIME && System.nanoTime() - firstWallclockTimestamp < passedTime) { @@ -105,9 +105,9 @@ public static void sendAfterRecord(final long timestamp, final long methodDurati orderIndex); } - private static void sendAfterGeneric(final byte ID, final long timestamp, + private static void sendAfterGeneric(final byte id, final long timestamp, final long methodDuration, final long traceId, final int orderIndex) { - EXPLORVIZ_BUFFER.put(ID); + EXPLORVIZ_BUFFER.put(id); EXPLORVIZ_BUFFER.putLong(methodDuration); EXPLORVIZ_BUFFER.putLong(traceId); EXPLORVIZ_BUFFER.putInt(orderIndex); @@ -125,9 +125,9 @@ public static void sendAfterFailedRecord(final long timestamp, final long method cause); } - private static void sendAfterFailedGeneric(final byte ID, final long timestamp, + private static void sendAfterFailedGeneric(final byte id, final long timestamp, final long methodDuration, final long traceId, final int orderIndex, final String cause) { - EXPLORVIZ_BUFFER.put(ID); + EXPLORVIZ_BUFFER.put(id); EXPLORVIZ_BUFFER.putLong(methodDuration); EXPLORVIZ_BUFFER.putLong(traceId); EXPLORVIZ_BUFFER.putInt(orderIndex); @@ -138,7 +138,7 @@ private static void sendAfterFailedGeneric(final byte ID, final long timestamp, /** * Mapping towards Kieker (ApplicationTraceMetaDataRecord) and ExplorViz - * (HostApplicationMetaDataRecord) + * (HostApplicationMetaDataRecord). * * @param timestamp (as configured in Kieker) * @param systemName (synthetic clustering of applications and nodes, is configured in ExplorViz) @@ -162,23 +162,23 @@ public static void sendApplicationTraceMetaDataRecord(final long timestamp, /** * Mapping towards Kieker (CPUUtilizationRecord and MemSwapUsageRecord) and ExplorViz - * (MemSwapUsageRecord) + * (MemSwapUsageRecord). * * @param timestamp (as configured in Kieker) * @param hostname (of the monitored application) * @param cpuUtilization (between 0 and 1) - * @param usedRAM (in byte) - * @param absoluteRAM (in byte) + * @param usedRam (in byte) + * @param absoluteRam (in byte) */ public static void sendSystemMonitoringRecord(final long timestamp, final String hostname, - final double cpuUtilization, final long usedRAM, final long absoluteRAM) { + final double cpuUtilization, final long usedRam, final long absoluteRam) { // a value of "0" marks that the information is not available, either memory or // cpu usage EXPLORVIZ_BUFFER.put(SystemMonitoringRecord.CLAZZ_ID); EXPLORVIZ_BUFFER.putDouble(cpuUtilization); - EXPLORVIZ_BUFFER.putLong(usedRAM); - EXPLORVIZ_BUFFER.putLong(absoluteRAM); + EXPLORVIZ_BUFFER.putLong(usedRam); + EXPLORVIZ_BUFFER.putLong(absoluteRam); sendBufferIfHasElements(timestamp); diff --git a/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/filter/teetime/KiekerToExplorVizTransformStage.java b/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/filter/teetime/KiekerToExplorVizTransformStage.java index 47bf536c..939d9dc1 100644 --- a/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/filter/teetime/KiekerToExplorVizTransformStage.java +++ b/kiekeradapter/src/main/java/net/explorviz/kiekeradapter/filter/teetime/KiekerToExplorVizTransformStage.java @@ -16,20 +16,14 @@ import kieker.common.record.system.CPUUtilizationRecord; import kieker.common.record.system.MemSwapUsageRecord; import net.explorviz.kiekeradapter.configuration.GenericExplorVizExternalLogAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import teetime.framework.AbstractConsumerStage; /** * Kieker Analysis Filter: Transforms Kieker Records to ExplorViz Records. * - * @author Christian Zirkelbach (czi@informatik.uni-kiel.de) - * */ public class KiekerToExplorVizTransformStage extends AbstractConsumerStage { - private static final Logger LOGGER = - LoggerFactory.getLogger(KiekerToExplorVizTransformStage.class.getName()); private final Stack stack = new Stack<>(); @Override @@ -64,11 +58,11 @@ public void inputKiekerRecords(final IMonitoringRecord kiekerRecord) { kiekerMetaDataRecord.getApplicationName(), programmingLanguage); } else if (kiekerRecord instanceof CPUUtilizationRecord) { - final CPUUtilizationRecord kiekerCPUUtilRecord = (CPUUtilizationRecord) kiekerRecord; - final String hostname = kiekerCPUUtilRecord.getHostname(); + final CPUUtilizationRecord kiekerCpuUtilRecord = (CPUUtilizationRecord) kiekerRecord; + final String hostname = kiekerCpuUtilRecord.getHostname(); - GenericExplorVizExternalLogAdapter.sendSystemMonitoringRecord(kiekerCPUUtilRecord - .getTimestamp(), hostname, kiekerCPUUtilRecord.getTotalUtilization(), 0, 0); + GenericExplorVizExternalLogAdapter.sendSystemMonitoringRecord(kiekerCpuUtilRecord + .getTimestamp(), hostname, kiekerCpuUtilRecord.getTotalUtilization(), 0, 0); } else if (kiekerRecord instanceof MemSwapUsageRecord) { final MemSwapUsageRecord kiekerMemUsageRecord = (MemSwapUsageRecord) kiekerRecord; final String hostname = kiekerMemUsageRecord.getHostname(); @@ -187,9 +181,8 @@ public void inputKiekerRecords(final IMonitoringRecord kiekerRecord) { methodDuration, kiekerAfter.getTraceId(), kiekerAfter.getOrderIndex()); - } - // DatabaseEvent records - else if (kiekerRecord instanceof BeforeDatabaseEvent) { + } else if (kiekerRecord instanceof BeforeDatabaseEvent) { + // DatabaseEvent records this.stack.push(kiekerRecord); final BeforeDatabaseEvent beforeDatabaseEvent = (BeforeDatabaseEvent) kiekerRecord; diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/AggregatedClazzCommunication.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/AggregatedClazzCommunication.java index 1d2d1c91..1632bd6b 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/AggregatedClazzCommunication.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/AggregatedClazzCommunication.java @@ -77,7 +77,12 @@ public void setAverageResponseTime(final float averageResponseTime) { this.averageResponseTime = averageResponseTime; } - // adds a clazzCommunication if sourceClazz and targetClazz matches + /** + * adds a clazzCommunication if sourceClazz and targetClazz matches. + * + * @param clazzcommunication the clazz communication to add + * @return {@code true} iff the communication was added + */ public boolean addClazzCommunication(final ClazzCommunication clazzcommunication) { if (this.sourceClazz.equals(clazzcommunication.getSourceClazz()) @@ -88,11 +93,4 @@ public boolean addClazzCommunication(final ClazzCommunication clazzcommunication } return false; } - - public void reset() { - this.totalRequests = 0; - this.clazzCommunications.clear(); - this.averageResponseTime = 0; - } - } diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/ClazzCommunication.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/ClazzCommunication.java index 1a0fc7a4..6bf4f07d 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/ClazzCommunication.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/ClazzCommunication.java @@ -38,7 +38,7 @@ public class ClazzCommunication extends BaseEntity { private int totalRequests; // average response time (for all involved related tracesteps) - private float averageResponseTime = 0; + private float averageResponseTime; @JsonCreator public ClazzCommunication(@JsonProperty("id") final String id) { @@ -77,7 +77,14 @@ public void setTraceSteps(final List traceSteps) { this.traceSteps = traceSteps; } - // returns a trace for a given traceId or creates a new one + /** + * Returns a trace for a given traceId or creates a new one. + * + * @param potentialNewTraceId the id to use if a new trace is created + * @param application the application to add the new trace to + * @param traceId the id of the trace + * @return the found or created trace + */ public Trace seekOrCreateTraceByTraceId(final String potentialNewTraceId, final Application application, final String traceId) { final List traces = application.getTraces(); @@ -94,7 +101,9 @@ public Trace seekOrCreateTraceByTraceId(final String potentialNewTraceId, return newTrace; } - // checks if a trace is existing and if not creates one and adds the runtime information + /** + * Checks if a trace is existing and if not creates one and adds the runtime information. + */ public void addTraceStep(final String potentialNewTraceId, final String traceStepId, final Application application, final String traceId, final int tracePosition, final int requests, final float averageResponseTime, final float currentTraceDuration) { @@ -123,6 +132,10 @@ public void setAverageResponseTime(final float averageRepsonseTime) { this.averageResponseTime = averageRepsonseTime; } + /** + * Resets the communication, i.e., sets {@link #totalRequests} and + * {@link #averageResponseTime} to {@code 0} + */ public void reset() { this.totalRequests = 0; this.averageResponseTime = 0; diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/Trace.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/Trace.java index 6f79618e..8d614b57 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/Trace.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/Trace.java @@ -32,7 +32,7 @@ public class Trace extends BaseEntity { @JsonCreator public Trace(@JsonProperty("id") final String id, @JsonProperty("traceId") final String traceId) { super(id); - this.setTraceId(traceId); + this.traceId = traceId; } /** diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/TraceStep.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/TraceStep.java index efbbb25a..72edb2b0 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/TraceStep.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/application/TraceStep.java @@ -22,7 +22,7 @@ public class TraceStep extends BaseEntity { private Trace parentTrace; // position in the related trace (first position = 1) - private int tracePosition = 0; + private int tracePosition; @Relationship("clazzCommunication") private ClazzCommunication clazzCommunication; @@ -54,8 +54,8 @@ public TraceStep(@JsonProperty("id") final String id, @JsonProperty("averageResponseTime") final float averageResponseTime, @JsonProperty("currentTraceDuration") final float currentTraceDuration) { super(id); - this.setParentTrace(parentTrace); - this.setClazzCommunication(clazzCommunication); + this.parentTrace = parentTrace; + this.clazzCommunication = clazzCommunication; this.setTracePosition(tracePosition); this.setAverageResponseTime(averageResponseTime); this.setRequests(requests); diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/event/Event.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/event/Event.java index 6ebeced4..6bf08794 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/event/Event.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/event/Event.java @@ -22,6 +22,9 @@ public class Event extends BaseEntity { private String eventMessage; + /** + * Constructs a new event. + */ @JsonCreator public Event(@JsonProperty("id") final String id, @JsonProperty("timestamp") final long timestamp, @JsonProperty("eventType") final EEventType eventType, diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/NameComperator.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/NameComperator.java index 70bcf1bc..78f0252a 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/NameComperator.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/NameComperator.java @@ -7,6 +7,9 @@ * Helper class for {@link NodeGroup}. */ public class NameComperator implements Comparator { + + private static final int BASE = 10; + @Override public int compare(final String o1, final String o2) { if (this.endsInNumber(o1) && this.endsInNumber(o2)) { @@ -19,14 +22,21 @@ public int compare(final String o1, final String o2) { } } + /** + * Calculates the last (that is rightmost) substring that is a numeric value (base 10). + * + * @param arg the string + * @return the numeric substring as + */ public double getLastNumber(final String arg) { + int i = arg.length() - 1; double result = 0d; int index = 0; while (i >= 0 && this.isNumber(arg.charAt(i))) { final int currentNumber = Integer.parseInt(arg.substring(i, i + 1)); - result = currentNumber * Math.pow(10, index) + result; + result = currentNumber * Math.pow(BASE, index) + result; i = i - 1; index = index + 1; } @@ -34,6 +44,12 @@ public double getLastNumber(final String arg) { return result; } + /** + * Check whether a string ends in a numeric value. + * + * @param arg the string + * @return {@code true} if the last characters of arg represent a numeric value base 10 + */ public boolean endsInNumber(final String arg) { if (arg == null) { return false; diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/TypeProvider.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/TypeProvider.java index 9ac20513..3a83bc0b 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/TypeProvider.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/helper/TypeProvider.java @@ -20,8 +20,17 @@ import net.explorviz.landscape.model.landscape.System; import net.explorviz.landscape.model.store.Timestamp; -public class TypeProvider { +/** + * Provides helper functions to manage ExplorViz core types as strings. + */ +public final class TypeProvider { + private TypeProvider(){/* Utility Class */} + + /** + * Returns a map where each K-V pair is the name name of an ExplorViz core type + * along with its implementing class. + */ public static Map> getExplorVizCoreTypesAsMap() { final Map> typeMap = new HashMap<>(); @@ -52,7 +61,7 @@ public static List> getExplorVizCoreTypesAsList() { public static Class[] getExplorVizCoreTypesAsArray() { final List> typeList = TypeProvider.getExplorVizCoreTypesAsList(); - return typeList.toArray(new Class[typeList.size()]); + return typeList.toArray(new Class[0]); } } diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/Node.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/Node.java index 3e0d1d30..99576d71 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/Node.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/Node.java @@ -23,8 +23,8 @@ public class Node extends BaseEntity { private String name; private String ipAddress; private double cpuUtilization; - private long freeRAM; - private long usedRAM; + private long freeRam; + private long usedRam; @Relationship("applications") private final List applications = new ArrayList<>(); @@ -69,20 +69,20 @@ public double getCpuUtilization() { return this.cpuUtilization; } - public void setFreeRAM(final long freeRAM) { - this.freeRAM = freeRAM; + public void setFreeRam(final long freeRam) { + this.freeRam = freeRam; } - public long getFreeRAM() { - return this.freeRAM; + public long getFreeRam() { + return this.freeRam; } - public void setUsedRAM(final long usedRAM) { - this.usedRAM = usedRAM; + public void setUsedRam(final long usedRam) { + this.usedRam = usedRam; } - public long getUsedRAM() { - return this.usedRAM; + public long getUsedRam() { + return this.usedRam; } public List getApplications() { diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/NodeGroup.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/NodeGroup.java index a393a882..a64e0c6f 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/NodeGroup.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/landscape/NodeGroup.java @@ -53,6 +53,10 @@ public List getNodes() { return this.nodes; } + /** + * Updates the name of the node group to a concatenation + * of names of all contained nodes. + */ public void updateName() { final List allNames = this.getNodeNames(); Collections.sort(allNames, new NameComperator()); @@ -72,10 +76,11 @@ public void updateName() { private List getNodeNames() { final List allNames = new ArrayList<>(); for (final Node node : this.nodes) { - if (node.getName() != null && !node.getName().isEmpty() && !node.getName().startsWith("<")) { // NOPMD - allNames.add(node.getName()); - } else { + if (node.getName() == null || node.getName().isEmpty() || node.getName() + .startsWith("<")) { // NOPMD allNames.add(node.getIpAddress()); + } else { + allNames.add(node.getName()); } } return allNames; diff --git a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/store/Timestamp.java b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/store/Timestamp.java index 7c7d8d0b..e21734c8 100644 --- a/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/store/Timestamp.java +++ b/landscape-service/landscape-model/src/main/java/net/explorviz/landscape/model/store/Timestamp.java @@ -18,24 +18,31 @@ @JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class, property = "super.id") public class Timestamp extends BaseEntity { - private long timestamp; + private long unixTimestamp; private int totalRequests; + /** + * Constructs a new timestamp. + * + * @param id the id + * @param timestampValue the actual unix epoch timestamp + * @param requests the total amount of requests at this point in time + */ @JsonCreator public Timestamp(@JsonProperty("id") final String id, - @JsonProperty("timestampValue") final long timestampValue, - @JsonProperty("requests") final int requests) { + @JsonProperty("timestampValue") final long timestampValue, + @JsonProperty("requests") final int requests) { super(id); - this.setTimestamp(timestampValue); + this.setUnixTimestamp(timestampValue); this.setTotalRequests(requests); } - public long getTimestamp() { - return this.timestamp; + public long getUnixTimestamp() { + return this.unixTimestamp; } - public void setTimestamp(final long timestamp) { - this.timestamp = timestamp; + public void setUnixTimestamp(final long unixTimestamp) { + this.unixTimestamp = unixTimestamp; } public int getTotalRequests() { @@ -48,7 +55,8 @@ public void setTotalRequests(final int totalRequests) { @Override public int hashCode() { - return new HashCodeBuilder().append(this.timestamp).append(this.totalRequests).append(this.id) + return new HashCodeBuilder().append(this.unixTimestamp).append(this.totalRequests) + .append(this.id) .build(); } @@ -64,18 +72,15 @@ public boolean equals(final Object obj) { return false; } final Timestamp other = (Timestamp) obj; - if (this.timestamp != other.timestamp) { + if (this.unixTimestamp != other.unixTimestamp) { return false; } - if (this.totalRequests != other.totalRequests) { - return false; - } - return true; + return this.totalRequests == other.totalRequests; } @Override public String toString() { - return new ToStringBuilder(this).append(this.id).append(this.timestamp).toString(); + return new ToStringBuilder(this).append(this.id).append(this.unixTimestamp).toString(); } diff --git a/landscape-service/landscape-model/src/test/java/net/explorviz/landscape/model/landscape/LandscapeTest.java b/landscape-service/landscape-model/src/test/java/net/explorviz/landscape/model/landscape/LandscapeTest.java index 98dad03f..9d30d350 100644 --- a/landscape-service/landscape-model/src/test/java/net/explorviz/landscape/model/landscape/LandscapeTest.java +++ b/landscape-service/landscape-model/src/test/java/net/explorviz/landscape/model/landscape/LandscapeTest.java @@ -1,7 +1,5 @@ package net.explorviz.landscape.model.landscape; - - import static org.junit.jupiter.api.Assertions.assertEquals; import net.explorviz.landscape.model.event.EEventType; @@ -10,13 +8,19 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +/** + * Tests the landscape model. + */ public class LandscapeTest { - private Landscape l; + // CHECKSTYLE.OFF: MagicNumberCheck + + private Landscape landscape; @BeforeEach public void setUp() { - this.l = new Landscape("1", new Timestamp("2", 1556558138878L, 300)); + this.landscape = + new Landscape("1", new Timestamp("2", 1556558138878L, 300)); } /** @@ -34,9 +38,9 @@ public void testEventCreation() { new Event("3", currentMillis, EEventType.NEWAPPLICATION, expectedEventMessage); // test the method and verify - this.l.createNewEvent("3", EEventType.NEWAPPLICATION, expectedEventMessage); + this.landscape.createNewEvent("3", EEventType.NEWAPPLICATION, expectedEventMessage); - final Event actualEvent = this.l.getEvents().get(0); + final Event actualEvent = this.landscape.getEvents().get(0); // the attributes must be equal assertEquals(expectedEvent.getEventType(), actualEvent.getEventType()); @@ -44,7 +48,7 @@ public void testEventCreation() { } /** - * Tests the creation of a new exception event + * Tests the creation of a new exception event. */ @Test public void testCreateNewException() { @@ -53,13 +57,14 @@ public void testCreateNewException() { final long currentMillis = java.lang.System.currentTimeMillis(); final String expectedCause = - "Exception thrown in application 'sampleApplication' by class 'boolean java.sql.Statement.execute(String)':\\n ..."; + "Exception thrown in application 'sampleApplication' by class " + + "'boolean java.sql.Statement.execute(String)':\\n ..."; final Event expectedEvent = new Event("4", currentMillis, EEventType.EXCEPTION, expectedCause); // test the method and verify - this.l.createNewException("4", expectedCause); + this.landscape.createNewException("4", expectedCause); - final Event actualEvent = this.l.getEvents().get(0); + final Event actualEvent = this.landscape.getEvents().get(0); // the attributes must be equal assertEquals(expectedEvent.getEventType(), actualEvent.getEventType()); diff --git a/landscape-service/src/main/java/net/explorviz/landscape/repository/InsertionRepositoryPart.java b/landscape-service/src/main/java/net/explorviz/landscape/repository/InsertionRepositoryPart.java index 624587a6..c51b94c4 100644 --- a/landscape-service/src/main/java/net/explorviz/landscape/repository/InsertionRepositoryPart.java +++ b/landscape-service/src/main/java/net/explorviz/landscape/repository/InsertionRepositoryPart.java @@ -16,6 +16,7 @@ import gnu.trove.set.hash.TIntHashSet; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Stack; import net.explorviz.landscape.model.application.Application; @@ -38,6 +39,9 @@ */ public class InsertionRepositoryPart { + // CHECKSTYLE.OFF: MultipleStringLiteralsCheck + // CHECKSTYLE.OFF: MagicNumberCheck + private static final String DEFAULT_COMPONENT_NAME = "(default)"; private final Map nodeCache = new HashMap<>(); @@ -54,12 +58,12 @@ public InsertionRepositoryPart(final IdGenerator idGen) { /** * Inserts a record into the data model (landscape). * - * @param inputIRecord - Record that will be inserted into the passed landscape - * @param landscape - Target for the insertion of records + * @param inputIRecord - Record that will be inserted into the passed landscape + * @param landscape - Target for the insertion of records * @param remoteCallRepositoryPart - TODOa */ public void insertIntoModel(final IRecord inputIRecord, final Landscape landscape, - final RemoteCallRepositoryPart remoteCallRepositoryPart) { + final RemoteCallRepositoryPart remoteCallRepositoryPart) { if (inputIRecord instanceof Trace) { final Trace trace = (Trace) inputIRecord; @@ -91,29 +95,28 @@ public void insertIntoModel(final IRecord inputIRecord, final Landscape landscap node.setParent(nodeGroup); nodeGroup.updateName(); - } else { - if (isNewApplication) { - // if new app, node might be placed in a different - // nodeGroup - - final NodeGroup oldNodeGroup = node.getParent(); - oldNodeGroup.getNodes().remove(node); - - final NodeGroup nodeGroup = this.seekOrCreateNodeGroup(system, node); - - if (!oldNodeGroup.equals(nodeGroup)) { - if (oldNodeGroup.getNodes().isEmpty()) { - oldNodeGroup.getParent().getNodeGroups().remove(oldNodeGroup); - } else { - oldNodeGroup.updateName(); - } - } + } else if (isNewApplication) { + // if new app, node might be placed in a different + // nodeGroup + + final NodeGroup oldNodeGroup = node.getParent(); + oldNodeGroup.getNodes().remove(node); - nodeGroup.getNodes().add(node); - node.setParent(nodeGroup); + final NodeGroup nodeGroup = this.seekOrCreateNodeGroup(system, node); - nodeGroup.updateName(); + if (!oldNodeGroup.equals(nodeGroup)) { + if (oldNodeGroup.getNodes().isEmpty()) { + oldNodeGroup.getParent().getNodeGroups().remove(oldNodeGroup); + } else { + oldNodeGroup.updateName(); + } } + + nodeGroup.getNodes().add(node); + node.setParent(nodeGroup); + + nodeGroup.updateName(); + } this.createCommuInApp(trace, @@ -133,13 +136,13 @@ public void insertIntoModel(final IRecord inputIRecord, final Landscape landscap if (node.getName() .equalsIgnoreCase(systemMonitoringRecord.getHostApplicationMetadata().getHostname()) && node.getIpAddress() - .equalsIgnoreCase( - systemMonitoringRecord.getHostApplicationMetadata().getIpaddress())) { + .equalsIgnoreCase( + systemMonitoringRecord.getHostApplicationMetadata().getIpaddress())) { node.setCpuUtilization(systemMonitoringRecord.getCpuUtilization()); - node.setFreeRAM( + node.setFreeRam( systemMonitoringRecord.getAbsoluteRAM() - systemMonitoringRecord.getUsedRAM()); - node.setUsedRAM(systemMonitoringRecord.getUsedRAM()); + node.setUsedRam(systemMonitoringRecord.getUsedRAM()); } } } @@ -149,7 +152,7 @@ public void insertIntoModel(final IRecord inputIRecord, final Landscape landscap /** * Seeks or creates a new system. * - * @param landscape - passed landscape + * @param landscape - passed landscape * @param systemname - name of the system * @return the retrieved or created system */ @@ -180,11 +183,11 @@ private System seekOrCreateSystem(final Landscape landscape, final String system * Seeks or creates a new node. * * @param hostApplicationRecord - monitoring information about the host - * @param landscape - the passed landscape + * @param landscape - the passed landscape * @return the retrieved or created node */ protected Node seekOrCreateNode(final HostApplicationMetaDataRecord hostApplicationRecord, - final Landscape landscape) { + final Landscape landscape) { final String nodeName = hostApplicationRecord.getHostname() + "_" + hostApplicationRecord.getIpaddress(); Node node = this.nodeCache.get(nodeName); @@ -213,7 +216,7 @@ protected Node seekOrCreateNode(final HostApplicationMetaDataRecord hostApplicat * Seeks or creates a new nodeGroup. * * @param system - the related system - * @param node - the related node + * @param node - the related node * @return the retrieved or created nodeGroup */ private NodeGroup seekOrCreateNodeGroup(final System system, final Node node) { @@ -237,9 +240,9 @@ private NodeGroup seekOrCreateNodeGroup(final System system, final Node node) { } /** - * Check whether two nodes are similar or not + * Check whether two nodes are similar or not. * - * @param node - the first node + * @param node - the first node * @param node2 - the second node * @return if the node is similar or not */ @@ -266,13 +269,14 @@ private boolean nodeMatchesNodeType(final Node node, final Node node2) { /** * Seeks or creates an application. * - * @param node - the related node + * @param node - the related node * @param hostMetaDataRecord - monitoring information about the host - * @param landscape - the related landscape + * @param landscape - the related landscape * @return the retrieved or created application */ - Application seekOrCreateApplication(final Node node, - final HostApplicationMetaDataRecord hostMetaDataRecord, final Landscape landscape) { + /* default */ Application seekOrCreateApplication(final Node node, + final HostApplicationMetaDataRecord hostMetaDataRecord, + final Landscape landscape) { final String applicationName = hostMetaDataRecord.getApplication(); Application application = this.applicationCache.get(node.getName() + "_" + applicationName); @@ -286,26 +290,37 @@ Application seekOrCreateApplication(final Node node, final String language = hostMetaDataRecord.getProgrammingLanguage(); - if ("JAVA".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.JAVA); - } else if ("C".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.C); - } else if ("CPP".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.CPP); - } else if ("CSHARP".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.CSHARP); - } else if ("PERL".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.PERL); - } else if ("JAVASCRIPT".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.JAVASCRIPT); - } else if ("PYTHON".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.PYTHON); - } else if ("RUBY".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.RUBY); - } else if ("PHP".equalsIgnoreCase(language)) { - application.setProgrammingLanguage(EProgrammingLanguage.PHP); - } else { - application.setProgrammingLanguage(EProgrammingLanguage.UNKNOWN); + switch (language.toUpperCase(Locale.ENGLISH)) { + case "JAVA": + application.setProgrammingLanguage(EProgrammingLanguage.JAVA); + break; + case "C": + application.setProgrammingLanguage(EProgrammingLanguage.C); + break; + case "CPP": + application.setProgrammingLanguage(EProgrammingLanguage.CPP); + break; + case "CSHARP": + application.setProgrammingLanguage(EProgrammingLanguage.CSHARP); + break; + case "PERL": + application.setProgrammingLanguage(EProgrammingLanguage.PERL); + break; + case "JAVASCRIPT": + application.setProgrammingLanguage(EProgrammingLanguage.JAVASCRIPT); + break; + case "PYTHON": + application.setProgrammingLanguage(EProgrammingLanguage.PYTHON); + break; + case "RUBY": + application.setProgrammingLanguage(EProgrammingLanguage.RUBY); + break; + case "PHP": + application.setProgrammingLanguage(EProgrammingLanguage.PHP); + break; + default: + application.setProgrammingLanguage(EProgrammingLanguage.UNKNOWN); + break; } application.setParent(node); @@ -326,16 +341,17 @@ Application seekOrCreateApplication(final Node node, /** * Communication between clazzes within a single application. * - * @param trace - the related trace - * @param currentHostname - the current hostname - * @param currentApplication - the current application - * @param landscape - the related landscape + * @param trace - the related trace + * @param currentHostname - the current hostname + * @param currentApplication - the current application + * @param landscape - the related landscape * @param remoteCallRepositoryPart - the RemoteCallRepositoryPart - * @param runtimeIndex - the position within the trace + * @param runtimeIndex - the position within the trace */ private void createCommuInApp(final Trace trace, final String currentHostname, - final Application currentApplication, final Landscape landscape, - final RemoteCallRepositoryPart remoteCallRepositoryPart, final int runtimeIndex) { + final Application currentApplication, final Landscape landscape, + final RemoteCallRepositoryPart remoteCallRepositoryPart, + final int runtimeIndex) { Clazz callerClazz = null; final Stack callerClazzesHistory = new Stack<>(); @@ -538,9 +554,11 @@ public static String getClazzName( } private void createOrUpdateCall(final Clazz caller, final Clazz callee, - final Application application, final int requests, final double average, - final double overallTraceDuration, final String traceId, final int orderIndex, - final String operationName, final Landscape landscape) { + final Application application, final int requests, + final double average, + final double overallTraceDuration, final String traceId, + final int orderIndex, + final String operationName, final Landscape landscape) { landscape.getTimestamp() .setTotalRequests(landscape.getTimestamp().getTotalRequests() + requests); @@ -563,7 +581,7 @@ private void createOrUpdateCall(final Clazz caller, final Clazz callee, } private Clazz seekOrCreateClazz(final String fullQName, final Application application, - final TIntHashSet objectIds) { + final TIntHashSet objectIds) { final String[] splittedName = fullQName.split("\\."); Map appCached = this.clazzCache.get(application); @@ -586,7 +604,8 @@ private Clazz seekOrCreateClazz(final String fullQName, final Application applic } private Clazz seekrOrCreateClazzHelper(final String fullQName, final String[] splittedName, - final Application application, final Component parent, final int index) { + final Application application, final Component parent, + final int index) { final String currentPart = splittedName[index]; Component potentialParent = parent; @@ -608,12 +627,13 @@ private Clazz seekrOrCreateClazzHelper(final String fullQName, final String[] sp } final Component component = new Component(this.idGen.generateId()); - String fullQNameComponent = ""; + StringBuilder fullQNameComponent = new StringBuilder(); for (int i = 0; i <= index; i++) { - fullQNameComponent += splittedName[i] + "."; + fullQNameComponent.append(splittedName[i]).append("."); } - fullQNameComponent = fullQNameComponent.substring(0, fullQNameComponent.length() - 1); - component.setFullQualifiedName(fullQNameComponent); + fullQNameComponent = + new StringBuilder(fullQNameComponent.substring(0, fullQNameComponent.length() - 1)); + component.setFullQualifiedName(fullQNameComponent.toString()); component.setName(currentPart); component.setParentComponent(potentialParent); component.setBelongingApplication(application); @@ -660,7 +680,7 @@ private Clazz seekrOrCreateClazzHelper(final String fullQName, final String[] sp } public static String getMethodName(final String operationSignatureStr, - final boolean constructor) { + final boolean constructor) { final Signature signature = SignatureParser.parse(operationSignatureStr, constructor); return signature.getOperationName(); } diff --git a/landscape-service/src/main/java/net/explorviz/landscape/repository/LandscapeRepositoryModel.java b/landscape-service/src/main/java/net/explorviz/landscape/repository/LandscapeRepositoryModel.java index 84a44131..bbb55031 100644 --- a/landscape-service/src/main/java/net/explorviz/landscape/repository/LandscapeRepositoryModel.java +++ b/landscape-service/src/main/java/net/explorviz/landscape/repository/LandscapeRepositoryModel.java @@ -88,7 +88,7 @@ private Landscape deepCopy(final Landscape original) throws DocumentSerializatio * Key functionality in the backend. Handles the persistence of a landscape every 10 seconds * passed. The employed time unit is defined as following in the Kieker configuration file * (monitoring.properties): - * + *

* TimeSource: 'kieker.monitoring.timer.SystemNanoTimer' Time in nanoseconds (with nanoseconds * precision) since Thu Jan 01 01:00:00 CET 1970' */ @@ -138,7 +138,7 @@ private void sendLandscapeToKafka(final Landscape l, final String kafkaTopicName LOGGER.debug( "Sending Kafka record with landscape id {}, timestamp {}, and payload to topic {}", l.getId(), - l.getTimestamp().getTimestamp(), + l.getTimestamp().getUnixTimestamp(), kafkaTopicName); } } catch (final DocumentSerializationException e) { diff --git a/landscape-service/src/main/java/net/explorviz/landscape/repository/RemoteCallRepositoryPart.java b/landscape-service/src/main/java/net/explorviz/landscape/repository/RemoteCallRepositoryPart.java index 5737668a..e608c616 100644 --- a/landscape-service/src/main/java/net/explorviz/landscape/repository/RemoteCallRepositoryPart.java +++ b/landscape-service/src/main/java/net/explorviz/landscape/repository/RemoteCallRepositoryPart.java @@ -24,7 +24,8 @@ public class RemoteCallRepositoryPart { private final Map sentRemoteCallRecordCache = new HashMap<>(); - private final Map receivedRemoteCallRecordCache = + private final Map + receivedRemoteCallRecordCache = new HashMap<>(); protected void checkForTimedoutRemoteCalls() { @@ -56,8 +57,9 @@ protected void checkForTimedoutRemoteCalls() { } public void insertSentRecord(final String potentialNewAppCommuId, final Clazz callerClazz, - final BeforeSentRemoteCallRecord sentRemoteCallRecord, final Landscape landscape, - final InsertionRepositoryPart inserter, final int runtimeIndex) { + final BeforeSentRemoteCallRecord sentRemoteCallRecord, + final Landscape landscape, + final InsertionRepositoryPart inserter, final int runtimeIndex) { final BeforeReceivedRemoteCallRecord receivedRecord = this.seekMatchingReceivedRemoteRecord(sentRemoteCallRecord); @@ -81,8 +83,10 @@ public void insertSentRecord(final String potentialNewAppCommuId, final Clazz ca } public void insertReceivedRecord(final String potentialNewAppCommuId, - final BeforeReceivedRemoteCallRecord receivedRemoteCallRecord, final Clazz firstReceiverClazz, - final Landscape landscape, final InsertionRepositoryPart inserter, final int runtimeIndex) { + final BeforeReceivedRemoteCallRecord receivedRemoteCallRecord, + final Clazz firstReceiverClazz, + final Landscape landscape, + final InsertionRepositoryPart inserter, final int runtimeIndex) { final BeforeSentRemoteCallRecord sentRecord = this.seekSentRemoteTraceIdandOrderId(receivedRemoteCallRecord); @@ -133,10 +137,13 @@ private BeforeSentRemoteCallRecord seekSentRemoteTraceIdandOrderId( // Communication between applications (landscape-perspective) private void seekOrCreateAppCommunication(final String potentialNewAppCommuId, - final BeforeSentRemoteCallRecord sentRemoteCallRecord, - final BeforeReceivedRemoteCallRecord receivedRemoteCallRecord, final Clazz sentRemoteClazz, - final Clazz receivedRemoteClazz, final Landscape landscape, - final InsertionRepositoryPart inserter, final int runtimeIndex) { + final BeforeSentRemoteCallRecord sentRemoteCallRecord, + final BeforeReceivedRemoteCallRecord receivedRemoteCallRecord, + final Clazz sentRemoteClazz, + final Clazz receivedRemoteClazz, + final Landscape landscape, + final InsertionRepositoryPart inserter, + final int runtimeIndex) { final Application callerApplication = this.getHostApplication(sentRemoteCallRecord, inserter, landscape); @@ -147,7 +154,7 @@ private void seekOrCreateAppCommunication(final String potentialNewAppCommuId, if (commu.getSourceApplication() == callerApplication && commu.getTargetApplication() == currentApplication || commu.getSourceApplication() == currentApplication - && commu.getTargetApplication() == callerApplication) { + && commu.getTargetApplication() == callerApplication) { commu.setRequests( commu.getRequests() + sentRemoteCallRecord.getRuntimeStatisticInformationList() .get(runtimeIndex) @@ -163,8 +170,8 @@ private void seekOrCreateAppCommunication(final String potentialNewAppCommuId, landscape.getTimestamp() .setTotalRequests(landscape.getTimestamp().getTotalRequests() + sentRemoteCallRecord.getRuntimeStatisticInformationList() - .get(runtimeIndex) - .getCount()); + .get(runtimeIndex) + .getCount()); return; } } @@ -193,12 +200,13 @@ private void seekOrCreateAppCommunication(final String potentialNewAppCommuId, landscape.getTimestamp() .setTotalRequests(landscape.getTimestamp().getTotalRequests() + sentRemoteCallRecord.getRuntimeStatisticInformationList() - .get(runtimeIndex) - .getCount()); + .get(runtimeIndex) + .getCount()); } public Application getHostApplication(final AbstractEventRecord record, - final InsertionRepositoryPart inserter, final Landscape landscape) { + final InsertionRepositoryPart inserter, + final Landscape landscape) { final HostApplicationMetaDataRecord hostMeta = record.getHostApplicationMetadataList().iterator().next(); final Node host = inserter.seekOrCreateNode(hostMeta, landscape); diff --git a/landscape-service/src/test/java/net/explorviz/landscape/injection/DefaultGeneratorInjectionTest.java b/landscape-service/src/test/java/net/explorviz/landscape/injection/DefaultGeneratorInjectionTest.java index 2e5c82de..9a026d31 100644 --- a/landscape-service/src/test/java/net/explorviz/landscape/injection/DefaultGeneratorInjectionTest.java +++ b/landscape-service/src/test/java/net/explorviz/landscape/injection/DefaultGeneratorInjectionTest.java @@ -1,7 +1,7 @@ package net.explorviz.landscape.injection; - import static org.junit.jupiter.api.Assertions.assertTrue; + import java.util.Properties; import javax.inject.Inject; import net.explorviz.landscape.server.main.DependencyInjectionBinder; diff --git a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/FlagSetting.java b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/FlagSetting.java index da22809f..03553c2f 100644 --- a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/FlagSetting.java +++ b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/FlagSetting.java @@ -51,10 +51,10 @@ public FlagSetting(final String displayName, final String description, final Str @SuppressWarnings("unused") private FlagSetting() { - // Morphia + super(); } - public boolean getDefaultValue() { + public boolean getDefaultValue() { // NOPMD return this.defaultValue; } @@ -91,7 +91,8 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return new HashCodeBuilder(17, 37).appendSuper(super.hashCode()) + + return new HashCodeBuilder(17, 37).appendSuper(super.hashCode()) // NOCS .append(this.defaultValue) .toHashCode(); } diff --git a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/RangeSetting.java b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/RangeSetting.java index 2697e384..41ea6f4d 100644 --- a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/RangeSetting.java +++ b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/RangeSetting.java @@ -66,6 +66,7 @@ public RangeSetting(final String displayName, final String description, final St @SuppressWarnings("unused") private RangeSetting() { // Morphia + super(); } @@ -124,7 +125,7 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return new HashCodeBuilder(17, 37).appendSuper(super.hashCode()) + return new HashCodeBuilder(17, 37).appendSuper(super.hashCode()) // NOCS .append(this.defaultValue) .append(this.min) .append(this.max) diff --git a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/Setting.java b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/Setting.java index df9a1039..690ea34b 100644 --- a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/Setting.java +++ b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/Setting.java @@ -21,21 +21,20 @@ @com.github.jasminb.jsonapi.annotations.Type("setting") @JsonSubTypes({@Type(name = "RangeSetting", value = RangeSetting.class), @Type(name = "FlagSeting", value = FlagSetting.class)}) -public abstract class Setting { +public abstract class Setting { // NOPMD /** * Contains all Subclasses of Setting, i.e., all concrete implementations of a Setting. */ @SuppressWarnings("serial") - public static final List> TYPES = - new ArrayList>() { - { - this.add(RangeSetting.class); - this.add(FlagSetting.class); - } - }; + public static final List> TYPES; + static { + TYPES = new ArrayList<>(); + TYPES.add(RangeSetting.class); + TYPES.add(FlagSetting.class); + } @Id @xyz.morphia.annotations.Id @@ -74,7 +73,6 @@ public Setting(final String id, final String displayName, final String descripti * @param origin the origin */ public Setting(final String displayName, final String description, final String origin) { - this.id = null; this.displayName = displayName; this.description = description; this.origin = origin; @@ -134,7 +132,7 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return new HashCodeBuilder(17, 37).append(this.id) + return new HashCodeBuilder(17, 37).append(this.id) // NOCS .append(this.displayName) .append(this.description) .append(this.origin) diff --git a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreference.java b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreference.java index 557c3f53..2d202700 100644 --- a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreference.java +++ b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreference.java @@ -68,8 +68,8 @@ public String toString() { .build(); } - @PreLoad - void fixup(final DBObject obj) { + @PreLoad // NOPMD + void fixup(final DBObject obj) { // NOPMD /* * this fixes morphia trying to cast value to a DBObject, which will fail in case of a primitive * Type (i.e. an integer can't be cast to DBObject). Thus we just take the raw value. @@ -99,7 +99,7 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return new HashCodeBuilder(17, 37).append(this.id) + return new HashCodeBuilder(17, 37).append(this.id) // NOCS .append(this.userId) .append(this.settingId) .append(this.value) diff --git a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreferenceConverter.java b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreferenceConverter.java index 12608c03..04ead3c4 100644 --- a/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreferenceConverter.java +++ b/settings-service/settings-model/src/main/java/net/explorviz/settings/model/UserPreferenceConverter.java @@ -5,7 +5,10 @@ import xyz.morphia.converters.TypeConverter; import xyz.morphia.mapping.MappedField; - +/** + * Custom converter to decode {@link UserPreference} object retrieved from a MongoDB instance. + * Used by Morphia. + */ public class UserPreferenceConverter extends TypeConverter implements SimpleValueConverter { diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceCreation.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceCreation.java index bd286970..f7f23b60 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceCreation.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceCreation.java @@ -1,31 +1,40 @@ package net.explorviz.settings.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; import java.io.IOException; import net.explorviz.security.user.User; import net.explorviz.settings.model.UserPreference; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiMapper; import net.explorviz.settings.server.resources.test.helper.UsersHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests the creation of preferences. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) public class PreferenceCreation { // private static final String USER_PREF_URL = // "http://localhost:8090/v1/users/{uid}/settings/preferences"; private static final String PREF_URL = "http://localhost:8090/v1/preferences"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; - private static String adminToken; private static String normieToken; // private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** @@ -35,24 +44,22 @@ public class PreferenceCreation { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { - adminToken = AuthorizationHelper.getAdminToken(); + public static void setUpAll() throws IOException { normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { - // this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); + public void setUp() { this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @Test - void createForSelf() { + public void createForSelf() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.appVizCommArrowSize.getId(); + final String settingId = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getId(); final double val = 2.0; // Default = 1.0, (0, 5) final UserPreference toCreate = new UserPreference(null, testUser.getId(), settingId, val); @@ -61,33 +68,33 @@ void createForSelf() { .getToken(); final Header auth = new Header("authorization", "Bearer " + myToken); - final UserPreference created = given().header(auth) + given().header(auth) .contentType(MEDIA_TYPE) - .body(toCreate, new JsonAPIMapper<>(UserPreference.class)) + .body(toCreate, new JsonApiMapper<>(UserPreference.class)) .when() .post(PREF_URL) .then() .statusCode(200) .extract() .body() - .as(UserPreference.class, new JsonAPIMapper<>(UserPreference.class)); + .as(UserPreference.class, new JsonApiMapper<>(UserPreference.class)); UsersHelper.getInstance().deleteUserById(testUser.getId()); } @Test - void createForOther() { + public void createForOther() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.appVizCommArrowSize.getId(); + final String settingId = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getId(); final double val = 2.0; // Default = 1.0, (0, 5) final UserPreference toCreate = new UserPreference(null, testUser.getId(), settingId, val); given().header(this.authHeaderNormie) .contentType(MEDIA_TYPE) - .body(toCreate, new JsonAPIMapper<>(UserPreference.class)) + .body(toCreate, new JsonApiMapper<>(UserPreference.class)) .when() .post(PREF_URL) .then() @@ -96,13 +103,13 @@ void createForOther() { } @Test - void createWithInvalidValue() { + public void createWithInvalidValue() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.appVizCommArrowSize.getId(); - final double val = DefaultSettings.appVizCommArrowSize.getMax() + 1; + final String settingId = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getId(); + final double val = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getMax() + 1; final UserPreference toCreate = new UserPreference(null, testUser.getId(), settingId, val); final String myToken = AuthorizationHelper.login("tester", "test") @@ -112,7 +119,7 @@ void createWithInvalidValue() { given().header(auth) .contentType(MEDIA_TYPE) - .body(toCreate, new JsonAPIMapper<>(UserPreference.class)) + .body(toCreate, new JsonApiMapper<>(UserPreference.class)) .when() .post(PREF_URL) .then() diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceDeletion.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceDeletion.java index 1dde5e97..abc6cbab 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceDeletion.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceDeletion.java @@ -1,32 +1,43 @@ package net.explorviz.settings.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; import java.io.IOException; +import net.explorviz.security.user.User; import net.explorviz.settings.model.UserPreference; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiMapper; import net.explorviz.settings.server.resources.test.helper.UsersHelper; -import net.explorviz.security.user.User; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests preference deletion. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) class PreferenceDeletion { private static final String USER_PREF_URL = "http://localhost:8090/v1/preferences?filter[user]={uid}"; private static final String PREF_URL = "http://localhost:8090/v1/preferences"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; + private static String adminToken; private static String normieToken; private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** @@ -36,39 +47,39 @@ class PreferenceDeletion { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } - private UserPreference setPref(final String uid, final String settingId, final Object value) { + private UserPreference updatePref(final String uid, final String settingId, final Object value) { final UserPreference up = new UserPreference(null, uid, settingId, value); return given().header(this.authHeaderAdmin) .contentType(MEDIA_TYPE) - .body(up, new JsonAPIMapper<>(UserPreference.class)) + .body(up, new JsonApiMapper<>(UserPreference.class)) .when() .post(PREF_URL) - .as(UserPreference.class, new JsonAPIMapper<>(UserPreference.class)); + .as(UserPreference.class, new JsonApiMapper<>(UserPreference.class)); } @Test - void deleteOwnPref() { + public void deleteOwnPref() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.keepHighlightingOnOpenOrClose.getId(); - final Boolean val = !DefaultSettings.keepHighlightingOnOpenOrClose.getDefaultValue(); - final UserPreference createdPref = this.setPref(testUser.getId(), settingId, val); + final String settingId = DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getId(); + final Boolean val = !DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getDefaultValue(); + final UserPreference createdPref = this.updatePref(testUser.getId(), settingId, val); final String myToken = AuthorizationHelper.login("tester", "test") .orElseThrow(IllegalStateException::new) @@ -93,23 +104,24 @@ void deleteOwnPref() { } /** - * All preference should be deleted if the corresponding user is removed + * All preference should be deleted if the corresponding user is removed. */ @Test - void testDeletionByUserDeletion() throws InterruptedException { + public void testDeletionByUserDeletion() throws InterruptedException { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.keepHighlightingOnOpenOrClose.getId(); - final Boolean val = !DefaultSettings.keepHighlightingOnOpenOrClose.getDefaultValue(); - this.setPref(testUser.getId(), settingId, val); + final String settingId = DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getId(); + final Boolean val = !DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getDefaultValue(); + this.updatePref(testUser.getId(), settingId, val); + // Delete User UsersHelper.getInstance().deleteUserById(testUser.getId()); - // Wait until event handled - Thread.sleep(500); + // Wait sufficient amount of time such that the event has been handled + Thread.sleep(2000); given().header(this.authHeaderAdmin) .when() @@ -123,14 +135,14 @@ void testDeletionByUserDeletion() throws InterruptedException { @Test - void deletePrefOfOtherUser() { + public void deletePrefOfOtherUser() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.keepHighlightingOnOpenOrClose.getId(); - final Boolean val = !DefaultSettings.keepHighlightingOnOpenOrClose.getDefaultValue(); - final String id = this.setPref(testUser.getId(), settingId, val).getId(); + final String settingId = DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getId(); + final Boolean val = !DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getDefaultValue(); + final String id = this.updatePref(testUser.getId(), settingId, val).getId(); given().header(this.authHeaderNormie) .contentType(MEDIA_TYPE) @@ -143,14 +155,14 @@ void deletePrefOfOtherUser() { } @Test - void deletePrefOfOtherUserAsAdmin() { + public void deletePrefOfOtherUserAsAdmin() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.keepHighlightingOnOpenOrClose.getId(); - final Boolean val = !DefaultSettings.keepHighlightingOnOpenOrClose.getDefaultValue(); - final String id = this.setPref(testUser.getId(), settingId, val).getId(); + final String settingId = DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getId(); + final Boolean val = !DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getDefaultValue(); + final String id = this.updatePref(testUser.getId(), settingId, val).getId(); given().header(this.authHeaderAdmin) .contentType(MEDIA_TYPE) diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceRetrieval.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceRetrieval.java index d7dc4094..f04fce88 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceRetrieval.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceRetrieval.java @@ -1,27 +1,37 @@ package net.explorviz.settings.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; import java.io.IOException; import java.util.List; +import net.explorviz.security.user.User; import net.explorviz.settings.model.UserPreference; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIListMapper; -import net.explorviz.settings.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiListMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiMapper; import net.explorviz.settings.server.resources.test.helper.UsersHelper; -import net.explorviz.security.user.User; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests preference retrieval. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) public class PreferenceRetrieval { private static final String USER_PREF_URL = "http://localhost:8090/v1/preferences?filter[user]={uid}"; private static final String PREF_URL = "http://localhost:8090/v1/preferences"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; private static String adminToken; private static String normieToken; @@ -29,7 +39,7 @@ public class PreferenceRetrieval { private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** @@ -39,13 +49,13 @@ public class PreferenceRetrieval { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @@ -55,7 +65,7 @@ private void setPref(final String uid, final String settingId, final Object valu given().header(this.authHeaderAdmin) .contentType(MEDIA_TYPE) - .body(up, new JsonAPIMapper<>(UserPreference.class)) + .body(up, new JsonApiMapper<>(UserPreference.class)) .when() .post(PREF_URL); } @@ -63,13 +73,13 @@ private void setPref(final String uid, final String settingId, final Object valu @Test @SuppressWarnings("unchecked") - void getOwnPrefs() { + public void retrieveOwnPrefs() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.keepHighlightingOnOpenOrClose.getId(); - final Boolean val = !DefaultSettings.keepHighlightingOnOpenOrClose.getDefaultValue(); + final String settingId = DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getId(); + final Boolean val = !DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getDefaultValue(); this.setPref(testUser.getId(), settingId, val); final String myToken = AuthorizationHelper.login("tester", "test") @@ -85,7 +95,7 @@ void getOwnPrefs() { .body("data.size()", CoreMatchers.is(1)) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(UserPreference.class)); + .as(List.class, new JsonApiListMapper<>(UserPreference.class)); final UserPreference pref = prefs.get(0); @@ -97,18 +107,12 @@ void getOwnPrefs() { } @Test - void getPrefsOfOtherUser() { + public void retrievePrefsOfOtherUser() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String user1token = AuthorizationHelper.login("tester", "test") - .orElseThrow(IllegalStateException::new) - .getToken(); - final Header auth = new Header("authorization", "Bearer " + user1token); - - given().header(this.authHeaderNormie) .when() .get(USER_PREF_URL.replace("{uid}", testUser.getId())) @@ -119,13 +123,13 @@ void getPrefsOfOtherUser() { } @Test - void getPrefsOfOtherAsAdmin() { + public void retrievePrefsOfOtherAsAdmin() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.keepHighlightingOnOpenOrClose.getId(); - final Boolean val = !DefaultSettings.keepHighlightingOnOpenOrClose.getDefaultValue(); + final String settingId = DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getId(); + final Boolean val = !DefaultSettings.KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE.getDefaultValue(); this.setPref(testUser.getId(), settingId, val); given().header(this.authHeaderAdmin) diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceUpdate.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceUpdate.java index 70fb7329..def247b1 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceUpdate.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/PreferenceUpdate.java @@ -1,24 +1,31 @@ package net.explorviz.settings.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; -import java.io.IOException; +import net.explorviz.security.user.User; import net.explorviz.settings.model.UserPreference; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiMapper; import net.explorviz.settings.server.resources.test.helper.UsersHelper; -import net.explorviz.security.user.User; import org.junit.Assert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests preference updates. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) class PreferenceUpdate { - private static final String USER_PREF_URL = - "http://localhost:8090/v1/preferences?filter[user]={uid}"; private static final String PREF_URL = "http://localhost:8090/v1/preferences"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; private static String adminToken; private static String normieToken; @@ -26,47 +33,46 @@ class PreferenceUpdate { private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** * Retrieves token for both an admin and an unprivileged user ("normie"). The default admin is * used for the former, a normie is created. * - * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } - private UserPreference setPref(final String uid, final String settingId, final Object value) { + private UserPreference createPref(final String uid, final String settingId, final Object value) { final UserPreference up = new UserPreference(null, uid, settingId, value); return given().header(this.authHeaderAdmin) .contentType(MEDIA_TYPE) - .body(up, new JsonAPIMapper<>(UserPreference.class)) + .body(up, new JsonApiMapper<>(UserPreference.class)) .when() .post(PREF_URL) - .as(UserPreference.class, new JsonAPIMapper<>(UserPreference.class)); + .as(UserPreference.class, new JsonApiMapper<>(UserPreference.class)); } @Test - void updateOwnPref() { + public void updateOwnPref() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.appVizCommArrowSize.getId(); + final String settingId = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getId(); final Double val = 2.0; // Default = 1.0, (0, 5) - final UserPreference createdPref = this.setPref(testUser.getId(), settingId, val); + final UserPreference createdPref = this.createPref(testUser.getId(), settingId, val); final UserPreference toUpdate = new UserPreference(createdPref.getId(), testUser.getId(), settingId, val + 1); @@ -78,14 +84,14 @@ void updateOwnPref() { final UserPreference updated = given().header(auth) .contentType(MEDIA_TYPE) - .body(toUpdate, new JsonAPIMapper<>(UserPreference.class)) + .body(toUpdate, new JsonApiMapper<>(UserPreference.class)) .when() .patch(PREF_URL + "/" + createdPref.getId()) .then() .statusCode(200) .extract() .body() - .as(UserPreference.class, new JsonAPIMapper<>(UserPreference.class)); + .as(UserPreference.class, new JsonApiMapper<>(UserPreference.class)); Assert.assertEquals(updated.getValue(), toUpdate.getValue()); Assert.assertEquals(updated, toUpdate); @@ -94,17 +100,17 @@ void updateOwnPref() { } @Test - void updateOwnPrefInvalidValue() { + public void updateOwnPrefInvalidValue() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.appVizCommArrowSize.getId(); + final String settingId = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getId(); final Double val = 2.0; // Default = 1.0, (0, 5) - final UserPreference createdPref = this.setPref(testUser.getId(), settingId, val); + final UserPreference createdPref = this.createPref(testUser.getId(), settingId, val); final UserPreference toUpdate = new UserPreference(createdPref.getId(), testUser.getId(), - settingId, DefaultSettings.appVizCommArrowSize.getMax() + 1); + settingId, DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getMax() + 1); final String myToken = AuthorizationHelper.login("tester", "test") .orElseThrow(IllegalStateException::new) @@ -113,7 +119,7 @@ void updateOwnPrefInvalidValue() { given().header(auth) .contentType(MEDIA_TYPE) - .body(toUpdate, new JsonAPIMapper<>(UserPreference.class)) + .body(toUpdate, new JsonApiMapper<>(UserPreference.class)) .when() .patch(PREF_URL + "/" + createdPref.getId()) .then() @@ -123,26 +129,21 @@ void updateOwnPrefInvalidValue() { } @Test - void updatePrefsOfOtherUser() { + public void updatePrefsOfOtherUser() { final User testUser = UsersHelper.getInstance() .createUser("tester", "test", null) .orElseThrow(IllegalStateException::new); - final String settingId = DefaultSettings.appVizCommArrowSize.getId(); + final String settingId = DefaultSettings.APP_VIZ_COMM_ARROW_SIZE.getId(); final Double val = 2.0; // Default = 1.0, (0, 5) - final UserPreference createdPref = this.setPref(testUser.getId(), settingId, val); + final UserPreference createdPref = this.createPref(testUser.getId(), settingId, val); final UserPreference toUpdate = new UserPreference(createdPref.getId(), testUser.getId(), settingId, val + 1); - final String myToken = AuthorizationHelper.login("tester", "test") - .orElseThrow(IllegalStateException::new) - .getToken(); - final Header auth = new Header("authorization", "Bearer " + myToken); - given().header(this.authHeaderNormie) .contentType(MEDIA_TYPE) - .body(toUpdate, new JsonAPIMapper<>(UserPreference.class)) + .body(toUpdate, new JsonApiMapper<>(UserPreference.class)) .when() .patch(PREF_URL + "/" + createdPref.getId()) .then() diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoCreation.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsCreation.java similarity index 79% rename from settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoCreation.java rename to settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsCreation.java index 7e78ce13..2630677a 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoCreation.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsCreation.java @@ -1,21 +1,32 @@ package net.explorviz.settings.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; import java.io.IOException; import net.explorviz.settings.model.FlagSetting; import net.explorviz.settings.model.Setting; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiMapper; import org.junit.Assert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class SettingsInfoCreation { +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests preference creation. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) +class SettingsCreation { private static final String SETTINGS_URL = "http://localhost:8090/v1/settings"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; + private static String adminToken; private static String normieToken; @@ -23,7 +34,6 @@ class SettingsInfoCreation { private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; /** @@ -33,33 +43,33 @@ class SettingsInfoCreation { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @Test - void createAsAdmin() { + public void createAsAdmin() { final Setting toCreate = - new FlagSetting("testname", "a test setting", DefaultSettings.origin, false); + new FlagSetting("testname", "a test setting", DefaultSettings.ORIGIN, false); final Setting created = given().header(this.authHeaderAdmin) .contentType(MEDIA_TYPE) - .body(toCreate, new JsonAPIMapper<>(Setting.class)) + .body(toCreate, new JsonApiMapper<>(Setting.class)) .when() .post(SETTINGS_URL) .then() .statusCode(200) .extract() .body() - .as(Setting.class, new JsonAPIMapper<>(Setting.class)); + .as(Setting.class, new JsonApiMapper<>(Setting.class)); // Delete to not affect other tests this.deleteSetting(created.getId()); @@ -70,12 +80,12 @@ void createAsAdmin() { } @Test - void createAsANormie() { + public void createAsANormie() { final Setting toCreate = - new FlagSetting("testname", "a test setting", DefaultSettings.origin, false); + new FlagSetting("testname", "a test setting", DefaultSettings.ORIGIN, false); given().header(this.authHeaderNormie) .contentType(MEDIA_TYPE) - .body(toCreate, new JsonAPIMapper<>(Setting.class)) + .body(toCreate, new JsonApiMapper<>(Setting.class)) .when() .post(SETTINGS_URL) .then() diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoDeletion.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsDeletion.java similarity index 80% rename from settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoDeletion.java rename to settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsDeletion.java index e7f87e1e..19f499b0 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoDeletion.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsDeletion.java @@ -1,20 +1,30 @@ package net.explorviz.settings.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; import java.io.IOException; import net.explorviz.settings.model.FlagSetting; import net.explorviz.settings.model.Setting; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class SettingsInfoDeletion { +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests settings deletion. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) +class SettingsDeletion { private static final String SETTINGS_URL = "http://localhost:8090/v1/settings"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; private static String adminToken; private static String normieToken; @@ -22,7 +32,7 @@ class SettingsInfoDeletion { private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** @@ -32,32 +42,32 @@ class SettingsInfoDeletion { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } private Setting create() { final Setting toCreate = - new FlagSetting("testname", "a test setting", DefaultSettings.origin, false); + new FlagSetting("testname", "a test setting", DefaultSettings.ORIGIN, false); final Setting created = given().header(this.authHeaderAdmin) .contentType(MEDIA_TYPE) - .body(toCreate, new JsonAPIMapper<>(Setting.class)) + .body(toCreate, new JsonApiMapper<>(Setting.class)) .when() .post(SETTINGS_URL) - .as(Setting.class, new JsonAPIMapper<>(Setting.class)); + .as(Setting.class, new JsonApiMapper<>(Setting.class)); return created; } @Test - void deleteAsAdmin() { + public void deleteAsAdmin() { final Setting toDelete = this.create(); given().header(this.authHeaderAdmin) @@ -69,7 +79,7 @@ void deleteAsAdmin() { } @Test - void deleteAsNormie() { + public void deleteAsNormie() { given().header(this.authHeaderNormie) .contentType(MEDIA_TYPE) .when() diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoRetrieval.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsRetrieval.java similarity index 79% rename from settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoRetrieval.java rename to settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsRetrieval.java index 5a54e328..c2c1a932 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsInfoRetrieval.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/SettingsRetrieval.java @@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; + import io.restassured.http.Header; import java.io.IOException; import java.util.List; @@ -12,7 +13,7 @@ import net.explorviz.settings.model.Setting; import net.explorviz.settings.server.resources.test.helper.AuthorizationHelper; import net.explorviz.settings.server.resources.test.helper.DefaultSettings; -import net.explorviz.settings.server.resources.test.helper.JsonAPIListMapper; +import net.explorviz.settings.server.resources.test.helper.JsonApiListMapper; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.jupiter.api.Assertions; @@ -20,9 +21,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class SettingsInfoRetrieval { +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests settings retrieval. + */ +@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.AvoidDuplicateLiterals"}) +public class SettingsRetrieval { private static final String SETTINGS_URL = "http://localhost:8090/v1/settings"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; private static String adminToken; private static String normieToken; @@ -30,7 +40,7 @@ public class SettingsInfoRetrieval { private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** @@ -40,13 +50,13 @@ public class SettingsInfoRetrieval { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @@ -54,20 +64,20 @@ void setUp() { @Test @SuppressWarnings("unchecked") - void getAllAsAdmin() { + public void retrieveAllAsAdmin() { final List retrieved = given().header(this.authHeaderNormie) .when() .get(SETTINGS_URL) .then() .statusCode(200) .body("$", hasKey("data")) - .body("data.size()", is(DefaultSettings.all.size())) + .body("data.size()", is(DefaultSettings.ALL.size())) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(Setting.class)); + .as(List.class, new JsonApiListMapper<>(Setting.class)); final List ids = retrieved.stream().map(Setting::getId).collect(Collectors.toList()); final List defaultIds = - DefaultSettings.all.stream().map(Setting::getId).collect(Collectors.toList()); + DefaultSettings.ALL.stream().map(Setting::getId).collect(Collectors.toList()); // All default ids should be returned in response Assertions.assertTrue(ids.stream().map(defaultIds::contains).reduce((a, b) -> a && b).get()); @@ -75,18 +85,18 @@ void getAllAsAdmin() { } @Test - void getAllAsNormie() { + public void retrieveAllAsNormie() { given().header(this.authHeaderAdmin) .when() .get(SETTINGS_URL) .then() .statusCode(200) .body("$", hasKey("data")) - .body("data.size()", is(DefaultSettings.all.size())); + .body("data.size()", is(DefaultSettings.ALL.size())); } @Test - void pagniationFirstPage() { + public void pagniationFirstPage() { final int size = 2; final int num = 0; given().contentType(MEDIA_TYPE) @@ -104,9 +114,9 @@ void pagniationFirstPage() { } @Test - void paginationMiddlePage() { + public void paginationMiddlePage() { final int size = 1; - final int num = DefaultSettings.all.size() / 2; + final int num = DefaultSettings.ALL.size() / 2; given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) .params("page[size]", size, "page[number]", num) @@ -123,8 +133,8 @@ void paginationMiddlePage() { @Test - void filterByOrigin() { - final String origin = DefaultSettings.origin; + public void filterByOrigin() { + final String origin = DefaultSettings.ORIGIN; given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) .params("filter[origin]", origin) @@ -132,13 +142,13 @@ void filterByOrigin() { .get(SETTINGS_URL) .then() .statusCode(200) - .body("data.size()", is(DefaultSettings.all.size())); + .body("data.size()", is(DefaultSettings.ALL.size())); } @Test @SuppressWarnings("unchecked") - void filterByType() { - final int rangeSettings = (int) DefaultSettings.all.stream() + public void filterByType() { + final int rangeSettings = (int) DefaultSettings.ALL.stream() .filter(s -> s.getClass().equals(RangeSetting.class)) .count(); final List retrieved = given().contentType(MEDIA_TYPE) @@ -151,7 +161,7 @@ void filterByType() { .body("data.size()", is(rangeSettings)) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(Setting.class)); + .as(List.class, new JsonApiListMapper<>(Setting.class)); final int retrievedRangeSettings = (int) retrieved.stream().filter(s -> s.getClass().equals(RangeSetting.class)).count(); diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/AuthorizationHelper.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/AuthorizationHelper.java index b922c64a..4f093a21 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/AuthorizationHelper.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/AuthorizationHelper.java @@ -1,26 +1,37 @@ package net.explorviz.settings.server.resources.test.helper; import static io.restassured.RestAssured.given; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; import com.github.jasminb.jsonapi.exceptions.ResourceParseException; import io.restassured.mapper.ObjectMapperType; import java.util.Optional; import net.explorviz.security.user.User; -public class AuthorizationHelper { +/** + * Utility class handle authorization. + */ +public final class AuthorizationHelper { private static final String AUTH_URL = "http://localhost:8090/v1/tokens/"; private static final String ADMIN_NAME = "admin"; private static final String NORMIE_NAME = "normie"; private static final String ADMIN_PW = "password"; - private static final String NORMIE_PW = "password"; + private static final String NORMIE_PW = ADMIN_PW; - private static String normieToken = null; - private static String adminToken = null; + private static User admin; + private static User normie; - private static User admin = null; - private static User normie = null; + private AuthorizationHelper(){/* Utility Class */} + /** + * Performs a login. + * @param name The username + * @param password the password + * @return an optional containing a user, iff the login was successful + */ public static Optional login(final String name, final String password) { try { @@ -28,7 +39,7 @@ public static Optional login(final String name, final String password) { .body(new UserCredentials(name, password), ObjectMapperType.JACKSON_2) .when() .post(AUTH_URL) - .as(User.class, new JsonAPIMapper<>(User.class)); + .as(User.class, new JsonApiMapper<>(User.class)); return Optional.of(u); } catch (final ResourceParseException ex) { return Optional.empty(); @@ -44,17 +55,20 @@ public static String getAdminToken() { return getAdmin().getToken(); } + /** + * Returns a user without any roles. + */ public static User getNormie() { final Optional normie = login(NORMIE_NAME, NORMIE_PW); if (AuthorizationHelper.normie == null) { if (normie.isPresent()) { AuthorizationHelper.normie = normie.get(); } else { - // Not existent, create and try again + // Not existing, create and try again // Will fail if normie user exists with another password - final Optional created_normie = + final Optional createdNormie = UsersHelper.getInstance().createUser(NORMIE_NAME, NORMIE_PW, null); - if (created_normie.isPresent()) { + if (createdNormie.isPresent()) { return getNormie(); } else { throw new IllegalStateException("Can no login as normie, does no exist"); @@ -65,6 +79,9 @@ public static User getNormie() { return AuthorizationHelper.normie; } + /** + * Returns a user with the admin role assigned. + */ public static User getAdmin() { if (AuthorizationHelper.admin == null) { final Optional admin = login(ADMIN_NAME, ADMIN_PW); @@ -77,11 +94,14 @@ public static User getAdmin() { return AuthorizationHelper.admin; } + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + public static class UserCredentials { + private String username; + private String password; - static class UserCredentials { - public String username; - public String password; + public UserCredentials() {/* Jackson */} + @JsonCreator public UserCredentials(final String username, final String password) { this.username = username; this.password = password; diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/DefaultSettings.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/DefaultSettings.java index d3265a67..abcfc0ed 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/DefaultSettings.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/DefaultSettings.java @@ -7,47 +7,51 @@ import net.explorviz.settings.model.RangeSetting; import net.explorviz.settings.model.Setting; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + /** - * Contains the default settings created at server startup + * Contains the default settings created at server startup. */ public class DefaultSettings { - public final static String origin = "backend"; + public static final String ORIGIN = "backend"; - public static FlagSetting showFpsCounter = new FlagSetting("showFpsCounter", "Show FPS Counter", - "\'Frames Per Second\' metrics in visualizations", origin, false); - public static FlagSetting appVizTransparency = new FlagSetting("appVizTransparency", + public static final FlagSetting + SHOW_FPS_COUNTER = new FlagSetting("showFpsCounter", "Show FPS Counter", + "\'Frames Per Second\' metrics in visualizations", ORIGIN, false); + public static final FlagSetting APP_VIZ_TRANSPARENCY = new FlagSetting("appVizTransparency", "Enable Transparent Components", - "Transparency effect for selection (left click) in application visualization", origin, true); - public static FlagSetting keepHighlightingOnOpenOrClose = new FlagSetting( + "Transparency effect for selection (left click) in application visualization", ORIGIN, true); + public static final FlagSetting KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE = new FlagSetting( "keepHighlightingOnOpenOrClose", "Keep Highlighting On Open Or Close", "Toggle if highlighting should be resetted on double click in application" + " visualization", - origin, true); - public static FlagSetting enableHoverEffects = new FlagSetting("enableHoverEffects", - "Enable Hover Effects", "Hover effect (flashing entities) for mouse cursor", origin, true); + ORIGIN, true); + public static final FlagSetting ENABLE_HOVER_EFFECTS = new FlagSetting("enableHoverEffects", + "Enable Hover Effects", "Hover effect (flashing entities) for mouse cursor", ORIGIN, true); - public static RangeSetting appVizCommArrowSize = new RangeSetting("appVizCommArrowSize", + public static final RangeSetting APP_VIZ_COMM_ARROW_SIZE = new RangeSetting("appVizCommArrowSize", "Arrow Size in Application Visualization", - "Arrow Size for selected communications in application visualization", origin, 1.0, 0.0, 5.0); - public static RangeSetting appVizTransparencyIntensity = new RangeSetting( + "Arrow Size for selected communications in application visualization", ORIGIN, 1.0, 0.0, 5.0); + public static final RangeSetting APP_VIZ_TRANSPARENCY_INTENSITY = new RangeSetting( "appVizTransparencyIntensity", "Transparency Intensity in Application Visualization", - "Transparency effect intensity (\'Enable Transparent Components\' must be enabled)", origin, + "Transparency effect intensity (\'Enable Transparent Components\' must be enabled)", ORIGIN, 0.1, 0.1, 0.5); - public static RangeSetting appVizCurvyCommHeight = + public static final RangeSetting APP_VIZ_CURVY_COMM_HEIGHT = new RangeSetting("appVizCurvyCommHeight", "Curviness of the Communication Lines", "If greater 0.0, communication lines are rendered arc-shaped with set height (Straight " + "lines: 0.0)", - origin, 0.0, 0.0, 50.0); + ORIGIN, 0.0, 0.0, 50.0); - public static List all = new ArrayList<>(Arrays.asList(showFpsCounter, - appVizTransparency, - keepHighlightingOnOpenOrClose, - enableHoverEffects, - appVizCommArrowSize, - appVizTransparencyIntensity, - appVizCurvyCommHeight)); + public static final List ALL = new ArrayList<>(Arrays.asList(SHOW_FPS_COUNTER, + APP_VIZ_TRANSPARENCY, + KEEP_HIGHLIGHTING_ON_OPEN_OR_CLOSE, + ENABLE_HOVER_EFFECTS, + APP_VIZ_COMM_ARROW_SIZE, + APP_VIZ_TRANSPARENCY_INTENSITY, + APP_VIZ_CURVY_COMM_HEIGHT)); } diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonAPIListMapper.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonApiListMapper.java similarity index 81% rename from settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonAPIListMapper.java rename to settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonApiListMapper.java index a6e422ba..f48c0477 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonAPIListMapper.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonApiListMapper.java @@ -12,12 +12,20 @@ import net.explorviz.settings.model.Setting; import net.explorviz.settings.model.UserPreference; -public class JsonAPIListMapper implements ObjectMapper { +/** + * Mapper for de/serialization of JSON:API collections into lists. + * @param the type to map + */ +public class JsonApiListMapper implements ObjectMapper { private final ResourceConverter converter; private final Class cls; - public JsonAPIListMapper(final Class cls) { + /** + * Creates a new mapper. + * @param cls the class of the type this mapper is for. + */ + public JsonApiListMapper(final Class cls) { this.cls = cls; this.converter = new ResourceConverter(); this.converter.registerType(Setting.class); @@ -42,9 +50,8 @@ public String serialize(final ObjectMapperSerializationContext context) { final byte[] serialized = this.converter.writeDocumentCollection(doc); return new String(serialized); } catch (final DocumentSerializationException e) { - e.printStackTrace(); + throw new IllegalStateException("Could not deserialize", e); } - return null; } } diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonAPIMapper.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonApiMapper.java similarity index 75% rename from settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonAPIMapper.java rename to settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonApiMapper.java index affb6ea6..5a64e45d 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonAPIMapper.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/JsonApiMapper.java @@ -7,18 +7,26 @@ import io.restassured.mapper.ObjectMapper; import io.restassured.mapper.ObjectMapperDeserializationContext; import io.restassured.mapper.ObjectMapperSerializationContext; +import net.explorviz.security.user.User; import net.explorviz.settings.model.FlagSetting; import net.explorviz.settings.model.RangeSetting; import net.explorviz.settings.model.Setting; import net.explorviz.settings.model.UserPreference; -import net.explorviz.security.user.User; -public class JsonAPIMapper implements ObjectMapper { +/** + * Mapper to de/serialize JSON:API into objects. + * @param The type + */ +public class JsonApiMapper implements ObjectMapper { private final ResourceConverter converter; private final Class cls; - public JsonAPIMapper(final Class cls) { + /** + * Creates a new mapper. + * @param cls the type to map + */ + public JsonApiMapper(final Class cls) { this.cls = cls; this.converter = new ResourceConverter(); this.converter.registerType(Setting.class); @@ -31,9 +39,8 @@ public JsonAPIMapper(final Class cls) { @Override public T deserialize(final ObjectMapperDeserializationContext context) { - final T deserialized = - this.converter.readDocument(context.getDataToDeserialize().asByteArray(), this.cls).get(); - return deserialized; + return this.converter.readDocument(context.getDataToDeserialize().asByteArray(), this.cls) + .get(); } @Override @@ -43,19 +50,22 @@ public String serialize(final ObjectMapperSerializationContext context) { final byte[] serialized = this.converter.writeDocument(doc); return new String(serialized); } catch (final DocumentSerializationException e) { - e.printStackTrace(); + throw new IllegalStateException("Could not serialize", e); } - return null; } + /** + * Serializes an object. + * @param object the object + * @return JSON:API string representation of the object + */ public String serializeRaw(final T object) { final JSONAPIDocument doc = new JSONAPIDocument<>(object); try { final byte[] serialized = this.converter.writeDocument(doc); return new String(serialized); } catch (final DocumentSerializationException e) { - e.printStackTrace(); + throw new IllegalStateException("Could not deserialize", e); } - return null; } } diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UserSerializationHelper.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UserSerializationHelper.java index d1806973..283573b9 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UserSerializationHelper.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UserSerializationHelper.java @@ -6,17 +6,23 @@ import java.io.IOException; import net.explorviz.security.user.User; -public class UserSerializationHelper { +/** + * Helper class to to serialize user objects. + */ +public final class UserSerializationHelper { + + + private UserSerializationHelper(){/* Utility class */} /** - * Serializes a user WITH the password attribute, which otherwise is ignored by Jackson + * Serializes a user WITH the password attribute, which otherwise is ignored by Jackson. * * @param u the user * @return a JSON:API string representing the user * @throws IOException if the json is invalid */ public static String serialize(final User u) throws IOException { - final String serialized = new JsonAPIMapper<>(User.class).serializeRaw(u); + final String serialized = new JsonApiMapper<>(User.class).serializeRaw(u); // Password is ignored we need to add it manually into the JSON tree final ObjectMapper mapper = new ObjectMapper(); diff --git a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UsersHelper.java b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UsersHelper.java index 4a48ab50..45a28036 100644 --- a/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UsersHelper.java +++ b/settings-service/src/apiTest/java/net/explorviz/settings/server/resources/test/helper/UsersHelper.java @@ -1,6 +1,7 @@ package net.explorviz.settings.server.resources.test.helper; import static io.restassured.RestAssured.given; + import com.github.jasminb.jsonapi.exceptions.ResourceParseException; import io.restassured.http.Header; import java.io.IOException; @@ -14,23 +15,19 @@ * Helper class for manipulating users through HTTP. Represents a minimal client to the user API. * All requests are performed as the default admin. */ -public class UsersHelper { +public final class UsersHelper { private static final Logger LOGGER = LoggerFactory.getLogger(UsersHelper.class); private static final String MEDIA_TYPE = "application/vnd.api+json"; - private final static String USERS_URI = "http://localhost:8090/v1/users/"; - - public static UsersHelper getInstance() { - if (instance == null) { - instance = new UsersHelper(); - } - return instance; - } + private static final String USERS_URI = "http://localhost:8090/v1/users/"; - private static UsersHelper instance = null; - private final Header auth; + private static class SingletonHolder { + private static final UsersHelper INSTANCE = new UsersHelper(); + } + + private final Header auth; // NOPMD private UsersHelper() { final String tok = AuthorizationHelper.getAdminToken(); @@ -38,17 +35,25 @@ private UsersHelper() { } + + public static UsersHelper getInstance() { + return SingletonHolder.INSTANCE; + } + + + + /** * Creates a new user by issuing a post request. * - * @param name name of the user + * @param name name of the user * @param password password of the user - * @param roles roles of the user + * @param roles roles of the user * @return An optional containing the created user as returned by the API or null if an error - * occured. + * occured. */ public Optional createUser(final String name, final String password, - final List roles) { + final List roles) { final User toCreate = new User(null, name, password, roles); try { @@ -57,7 +62,7 @@ public Optional createUser(final String name, final String password, .header(this.auth) .when() .post(USERS_URI) - .as(User.class, new JsonAPIMapper<>(User.class)); + .as(User.class, new JsonApiMapper<>(User.class)); return Optional.of(u); } catch (final IOException e) { return Optional.empty(); @@ -68,7 +73,7 @@ public Optional createUser(final String name, final String password, } /** - * Delete a user by id + * Delete a user by id. * * @param id if of the user to delete */ @@ -76,13 +81,15 @@ public void deleteUserById(final String id) { given().contentType(MEDIA_TYPE).header(this.auth).when().delete(USERS_URI + id); } - + /** + * Returns all users. + */ public List getAll() { return given().contentType(MEDIA_TYPE) .header(this.auth) .when() .get(USERS_URI) - .as(List.class, new JsonAPIListMapper<>(User.class)); + .as(List.class, new JsonApiListMapper<>(User.class)); } public int count() { diff --git a/settings-service/src/main/java/net/explorviz/settings/server/inject/DatastoreFactory.java b/settings-service/src/main/java/net/explorviz/settings/server/inject/DatastoreFactory.java index 42f446ea..7cb969d5 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/inject/DatastoreFactory.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/inject/DatastoreFactory.java @@ -11,7 +11,9 @@ import xyz.morphia.Datastore; import xyz.morphia.Morphia; - +/** + * Factory for Morphia {@link Datastore} to use for injection. + */ public class DatastoreFactory implements Factory { private final Datastore datastore; diff --git a/settings-service/src/main/java/net/explorviz/settings/server/main/Application.java b/settings-service/src/main/java/net/explorviz/settings/server/main/Application.java index 0d920509..d296e3fb 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/main/Application.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/main/Application.java @@ -5,7 +5,7 @@ import net.explorviz.settings.model.Setting; import net.explorviz.settings.model.UserPreference; import net.explorviz.settings.server.providers.SettingJsonApiDeserializer; -import net.explorviz.settings.server.providers.UserSettingJsonApiDeserializer; +import net.explorviz.settings.server.providers.UserPreferenceJsonApiDeserializer; import net.explorviz.settings.server.resources.EntryPointResource; import net.explorviz.settings.server.resources.SettingsResource; import net.explorviz.settings.server.resources.UserPreferencesResource; @@ -24,17 +24,21 @@ import net.explorviz.shared.security.filters.CorsResponseFilter; import org.glassfish.jersey.server.ResourceConfig; +/** + * Configuration for the Settings-Service web application used to boot the JAX-RS servlet. + */ public class Application extends ResourceConfig { + /** + * Initiates a new applications configuration. + */ public Application() { - + super(); GenericTypeFinder.getTypeMap().put("Setting", Setting.class); GenericTypeFinder.getTypeMap().put("RangeSetting", RangeSetting.class); GenericTypeFinder.getTypeMap().put("FlagSetting", FlagSetting.class); GenericTypeFinder.getTypeMap().put("UserPreference", UserPreference.class); - - // register CDI this.register(new DependencyInjectionBinder()); @@ -54,7 +58,7 @@ public Application() { // JSON API Serializing this.register(SettingJsonApiDeserializer.class); - this.register(UserSettingJsonApiDeserializer.class); + this.register(UserPreferenceJsonApiDeserializer.class); this.register(JsonApiProvider.class); this.register(JsonApiListProvider.class); this.register(ResourceConverterFactory.class); diff --git a/settings-service/src/main/java/net/explorviz/settings/server/main/Main.java b/settings-service/src/main/java/net/explorviz/settings/server/main/Main.java index 0b44163d..bf3f8479 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/main/Main.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/main/Main.java @@ -9,7 +9,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Main { +/** + * Contains the entry point which boots the Settings-Service. + */ +public final class Main { private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); private static final int DEFAULT_PORT = 8084; diff --git a/settings-service/src/main/java/net/explorviz/settings/server/providers/SettingJsonApiDeserializer.java b/settings-service/src/main/java/net/explorviz/settings/server/providers/SettingJsonApiDeserializer.java index fb5471d0..52010dab 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/providers/SettingJsonApiDeserializer.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/providers/SettingJsonApiDeserializer.java @@ -1,7 +1,6 @@ package net.explorviz.settings.server.providers; import com.github.jasminb.jsonapi.ResourceConverter; -import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; @@ -12,6 +11,9 @@ import javax.ws.rs.ext.MessageBodyReader; import net.explorviz.settings.model.Setting; +/** + * {@link MessageBodyReader} for {@link Setting} entities which consumes JSON:API strings. + */ public class SettingJsonApiDeserializer implements MessageBodyReader { private final ResourceConverter converter; @@ -32,11 +34,9 @@ public boolean isReadable(final Class type, final Type genericType, public Setting readFrom(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap httpHeaders, final InputStream entityStream) - throws IOException, WebApplicationException { + throws WebApplicationException { - final Setting setting = this.converter.readDocument(entityStream, type).get(); - - return setting; + return this.converter.readDocument(entityStream, type).get(); } } diff --git a/settings-service/src/main/java/net/explorviz/settings/server/providers/UserSettingJsonApiDeserializer.java b/settings-service/src/main/java/net/explorviz/settings/server/providers/UserPreferenceJsonApiDeserializer.java similarity index 51% rename from settings-service/src/main/java/net/explorviz/settings/server/providers/UserSettingJsonApiDeserializer.java rename to settings-service/src/main/java/net/explorviz/settings/server/providers/UserPreferenceJsonApiDeserializer.java index 3ce23b2c..6da8b942 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/providers/UserSettingJsonApiDeserializer.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/providers/UserPreferenceJsonApiDeserializer.java @@ -1,7 +1,6 @@ package net.explorviz.settings.server.providers; import com.github.jasminb.jsonapi.ResourceConverter; -import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; @@ -11,21 +10,23 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import net.explorviz.settings.model.UserPreference; -import xyz.morphia.Datastore; -public class UserSettingJsonApiDeserializer implements MessageBodyReader { +/** + * {@link MessageBodyReader} which consumes JSON:API strings + * and deserializes them into {@link UserPreference} entities. + */ +public class UserPreferenceJsonApiDeserializer implements MessageBodyReader { private final ResourceConverter converter; @Inject - public UserSettingJsonApiDeserializer(final ResourceConverter converter, - final Datastore datastore) { + public UserPreferenceJsonApiDeserializer(final ResourceConverter converter) { this.converter = converter; } @Override public boolean isReadable(final Class type, final Type genericType, - final Annotation[] annotations, final MediaType mediaType) { + final Annotation[] annotations, final MediaType mediaType) { // TODO Auto-generated method stub return true; } @@ -33,12 +34,12 @@ public boolean isReadable(final Class type, final Type genericType, @Override public UserPreference readFrom(final Class type, final Type genericType, - final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap httpHeaders, final InputStream entityStream) - throws IOException, WebApplicationException { + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, + final InputStream entityStream) + throws WebApplicationException { - final UserPreference setting = this.converter.readDocument(entityStream, type).get(); - return setting; + return this.converter.readDocument(entityStream, type).get(); } } diff --git a/settings-service/src/main/java/net/explorviz/settings/server/resources/SettingsResource.java b/settings-service/src/main/java/net/explorviz/settings/server/resources/SettingsResource.java index a940a934..20b4dc11 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/resources/SettingsResource.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/resources/SettingsResource.java @@ -143,10 +143,9 @@ public Response deleteById( description = "Setting to create. The id must not be set.") public Setting createSetting(final Setting s) { try { - final Setting updated = this.repo.createOrUpdate(s); - return updated; - } catch (final Exception e) { - throw new BadRequestException(e.getMessage()); + return this.repo.createOrUpdate(s); + } catch (final IllegalArgumentException e) { + throw new BadRequestException(e.getMessage(), e); } } diff --git a/settings-service/src/main/java/net/explorviz/settings/server/resources/UserPreferencesResource.java b/settings-service/src/main/java/net/explorviz/settings/server/resources/UserPreferencesResource.java index 31780fa0..03d4b919 100644 --- a/settings-service/src/main/java/net/explorviz/settings/server/resources/UserPreferencesResource.java +++ b/settings-service/src/main/java/net/explorviz/settings/server/resources/UserPreferencesResource.java @@ -211,13 +211,11 @@ public UserPreference updatePref( this.userPrefService.validate(updatedPref); } catch (final PreferenceValidationException e) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Invalid preference: %s", e.getMessage()); + LOGGER.debug("Invalid preference: {}", e.getMessage()); } - throw new BadRequestException(e.getMessage()); + throw new BadRequestException(e.getMessage(), e); } - - found.setValue(updatedPref.getValue()); this.userPrefRepo.createOrUpdate(found); @@ -262,14 +260,14 @@ public UserPreference createPreference(final UserPreference preference, this.userPrefService.validate(preference); } catch (final PreferenceValidationException e) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Invalid preference: %s", e.getMessage()); + LOGGER.debug("Invalid preference: {}", e.getMessage(), e); } - throw new BadRequestException(e.getMessage()); + throw new BadRequestException(e.getMessage(), e); } try { return this.userPrefRepo.createOrUpdate(preference); } catch (final IllegalStateException e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException(e.getMessage(), e); } } diff --git a/settings-service/src/main/java/net/explorviz/settings/services/AuthorizationService.java b/settings-service/src/main/java/net/explorviz/settings/services/AuthorizationService.java index ba4f9f60..24fa3321 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/AuthorizationService.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/AuthorizationService.java @@ -1,13 +1,13 @@ package net.explorviz.settings.services; +import java.util.Locale; import javax.inject.Inject; -import net.explorviz.shared.security.TokenParserService; import net.explorviz.shared.security.TokenDetails; +import net.explorviz.shared.security.TokenParserService; import org.jvnet.hk2.annotations.Service; /** * Handles authorization. - * */ @Service public class AuthorizationService { @@ -22,7 +22,7 @@ public AuthorizationService(final TokenParserService tokenParserService) { /** * Checks whether the given user id matches to the one given in the auth header. * - * @param uid user id + * @param uid user id * @param authHeader authorization header * @return if and only if the user id is the same as the id given in the authorization header */ @@ -30,7 +30,8 @@ public boolean isSameUser(final String uid, final String authHeader) { try { final TokenDetails details = this.tps.parseToken(authHeader.substring(7)); return details.getUserId().contentEquals(uid); - } catch (final NullPointerException e) { + } catch (final NullPointerException e) { // NOPMD + // Todo: where does this NPE come from? // No token given return false; } @@ -45,8 +46,11 @@ public boolean isSameUser(final String uid, final String authHeader) { public boolean isAdmin(final String authHeader) { try { final TokenDetails details = this.tps.parseToken(authHeader.substring(7)); - return details.getRoles().stream().map(r -> r.toLowerCase()).anyMatch(r -> r.equals("admin")); - } catch (final NullPointerException e) { + return details.getRoles().stream() + .map(s -> s.toLowerCase(Locale.ENGLISH)) + .anyMatch("admin"::equals); + } catch (final NullPointerException e) { // NOPMD + // Todo: where does this NPE come from? // No token return false; } diff --git a/settings-service/src/main/java/net/explorviz/settings/services/SettingsRepository.java b/settings-service/src/main/java/net/explorviz/settings/services/SettingsRepository.java index b4544d3f..5642f192 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/SettingsRepository.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/SettingsRepository.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.stream.Collectors; import javax.inject.Inject; @@ -29,6 +30,9 @@ public class SettingsRepository implements MongoRepository, Que private static final Logger LOGGER = LoggerFactory.getLogger(SettingsRepository.class); + private static final String FILTER_ORIGIN_FIELD = "origin"; + private static final String FILTER_TYPE_FIELD = "type"; + private final IdGenerator idgen; private final Datastore datastore; @@ -148,20 +152,15 @@ public void createOrOverride(final Setting setting) { @Override public QueryResult query(final net.explorviz.shared.querying.Query query) { - final String originField = "origin"; - final String typeField = "type"; - - String originFilter = null; - List> classes = new ArrayList<>(Setting.TYPES); - if (query.getFilters().get(typeField) != null) { - if (query.getFilters().get(typeField).size() == 1) { + if (query.getFilters().get(FILTER_TYPE_FIELD) != null) { + if (query.getFilters().get(FILTER_TYPE_FIELD).size() == 1) { // Reduce the subclasses to process to only contain the class filtered for - final String typeFilter = query.getFilters().get(typeField).get(0); + final String typeFilter = query.getFilters().get(FILTER_TYPE_FIELD).get(0); classes = classes.stream() - .filter(c -> c.getSimpleName().toLowerCase().contentEquals(typeFilter)) + .filter(c -> c.getSimpleName().toLowerCase(Locale.ENGLISH).contentEquals(typeFilter)) .collect(Collectors.toList()); } else { // Filters work conjunctive and settings can only be of a single type @@ -170,9 +169,10 @@ public QueryResult query(final net.explorviz.shared.querying.Query query(final net.explorviz.shared.querying.Query cls : classes) { - final xyz.morphia.query.Query q = (Query) this.datastore.createQuery(cls); + final Query q = (Query) this.datastore.createQuery(cls); if (originFilter != null) { - q.field(originField).contains(originFilter); + q.field(FILTER_ORIGIN_FIELD).contains(originFilter); } data.addAll(q.asList()); } diff --git a/settings-service/src/main/java/net/explorviz/settings/services/UnknownSettingException.java b/settings-service/src/main/java/net/explorviz/settings/services/UnknownSettingException.java deleted file mode 100644 index eb9549fa..00000000 --- a/settings-service/src/main/java/net/explorviz/settings/services/UnknownSettingException.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.explorviz.settings.services; - -/** - * Exception that a setting, that was searched for, does not exist. - * - */ -@SuppressWarnings("serial") -public class UnknownSettingException extends Exception { - - public UnknownSettingException() { - super(); - } - -} diff --git a/settings-service/src/main/java/net/explorviz/settings/services/UserPreferenceRepository.java b/settings-service/src/main/java/net/explorviz/settings/services/UserPreferenceRepository.java index ead7389a..732c3f85 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/UserPreferenceRepository.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/UserPreferenceRepository.java @@ -26,6 +26,8 @@ public class UserPreferenceRepository private static final Logger LOGGER = LoggerFactory.getLogger(SettingsRepository.class); + private static final String UID_FIELD = "userId"; + private static final String FILTER_USER_FIELD = "user"; private final Datastore datastore; @@ -98,18 +100,17 @@ public void delete(final String id) { @Override public QueryResult query(final Query query) { - final String userIdField = "userId"; - final String userFilterField = "user"; + final xyz.morphia.query.Query q = this.datastore.createQuery(UserPreference.class); // Filter for user - final List userFilter = query.getFilters().get(userFilterField); + final List userFilter = query.getFilters().get(FILTER_USER_FIELD); if (userFilter != null && userFilter.size() == 1) { - q.field(userIdField).contains(userFilter.get(0)); + q.field(UID_FIELD).contains(userFilter.get(0)); } diff --git a/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventConsumer.java b/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventConsumer.java index bd3ff102..0ce96fa6 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventConsumer.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventConsumer.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.time.Duration; -import java.util.Arrays; +import java.util.Collections; import java.util.Properties; import net.explorviz.shared.config.annotations.Config; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -17,7 +17,6 @@ /** * Consumes user events published on Kafka. - * */ @Service public class UserEventConsumer implements Runnable { @@ -30,6 +29,12 @@ public class UserEventConsumer implements Runnable { private UserEventHandler handler; + /** + * Creates a new consumer that ingests user events from kafka once run. + * + * @param kafkaBootStrapServerList the bootstrap server + * @param groupId the kafka group id to use + */ public UserEventConsumer( @Config("exchange.kafka.bootstrap.servers") final String kafkaBootStrapServerList, @Config("exchange.kafka.group.id") final String groupId) { @@ -39,7 +44,8 @@ public UserEventConsumer( properties.put("group.id", groupId); properties.put("enable.auto.commit", "true"); properties.put("auto.commit.interval.ms", "1000"); - properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");// NOCS + properties + .put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");// NOCS properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); @@ -54,8 +60,8 @@ public void setHandler(final UserEventHandler handler) { public void run() { LOGGER.info("Starting Kafka Exchange \n"); - this.kafkaConsumer.subscribe(Arrays.asList(TOPIC)); - + this.kafkaConsumer.subscribe(Collections.singletonList(TOPIC)); + final ObjectMapper mapper = new ObjectMapper(); while (true) { final ConsumerRecords records = this.kafkaConsumer.poll(Duration.ofMillis(100)); @@ -65,15 +71,17 @@ public void run() { LOGGER.debug("Recevied landscape Kafka record: {}", record.value()); final String serializedUserEvent = record.value(); - - final ObjectMapper mapper = new ObjectMapper(); - UserEvent e = null; + UserEvent e; try { e = mapper.readValue(serializedUserEvent, UserEvent.class); - LOGGER.info("Received User Event: " + e.toString()); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Received User Event: " + e.toString()); + } this.notifyHandler(e); } catch (final IOException ex) { - LOGGER.error("Could not deserialize value", ex); + if (LOGGER.isErrorEnabled()) { + LOGGER.error("Could not deserialize value", ex); + } } } } @@ -87,30 +95,37 @@ private void notifyHandler(final UserEvent event) { break; case DELETED: this.handler.onDelete(event.getId()); + break; + default: + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Received unknown user event: {}", event.getEvent()); + } + break; } } } - static class UserEvent { + /** + * User events read from Kafka topic. + */ + public static class UserEvent { + /** + * Denotes which type of event occurred for the user of given id. + */ public enum EventType { CREATED, DELETED; } + @JsonProperty("event") private EventType event; @JsonProperty("id") private String id; - - public UserEvent(final EventType event, final String id) { - this.event = event; - this.id = id; - } - - public UserEvent() { - + public UserEvent() { // NOPMD + /* Jackson */ } public EventType getEvent() { diff --git a/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventHandlerImpl.java b/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventHandlerImpl.java index 9e976463..afa302c7 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventHandlerImpl.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/kafka/UserEventHandlerImpl.java @@ -8,6 +8,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Handler for user events as received by {@link UserEventConsumer}. + */ public class UserEventHandlerImpl implements UserEventHandler { private static final Logger LOGGER = LoggerFactory.getLogger(UserEventHandlerImpl.class); @@ -27,7 +30,7 @@ public void onDelete(final String userId) { // Delete all preference associated with the deleted user final List prefs = this.ups.getPreferencesForUser(userId); - prefs.stream().map(p -> p.getId()).forEach(i -> this.upr.delete(i)); + prefs.stream().map(UserPreference::getId).forEach(this.upr::delete); LOGGER.info(String.format("Deleted %d preferences for user with id %s", prefs.size(), userId)); } diff --git a/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidationException.java b/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidationException.java index 60bdc492..5b15ed9f 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidationException.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidationException.java @@ -4,30 +4,15 @@ /** * Exception that indicates that the validation of a {@link UserPreference} has failed. - * */ @SuppressWarnings("serial") public class PreferenceValidationException extends Exception { - public PreferenceValidationException() { - - } - public PreferenceValidationException(final String arg0) { super(arg0); } - public PreferenceValidationException(final Throwable arg0) { - super(arg0); + public PreferenceValidationException(final String s, final Throwable cause) { + super(s, cause); } - - public PreferenceValidationException(final String arg0, final Throwable arg1) { - super(arg0, arg1); - } - - public PreferenceValidationException(final String arg0, final Throwable arg1, final boolean arg2, - final boolean arg3) { - super(arg0, arg1, arg2, arg3); - } - } diff --git a/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidator.java b/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidator.java index 82891ec4..cdf17e0c 100644 --- a/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidator.java +++ b/settings-service/src/main/java/net/explorviz/settings/services/validation/PreferenceValidator.java @@ -23,7 +23,7 @@ public class PreferenceValidator { /** * Creates a new validator specific for the given {@link UserPreference}. The validator works by - * checking all constraints defined in the given {@link FlagSetting} object hold regarding the + * checking all constraints defined in the given {@link Setting} object hold regarding the * given user preference. * * @param userPreference the user preference object @@ -64,12 +64,12 @@ public void validate(final Setting setting) throws PreferenceValidationException * @throws PreferenceValidationException thrown if the preference is invalid according to the * setting object. The message of the exception contains the reason. */ + @SuppressWarnings("unused") private void validate(final FlagSetting setting) throws PreferenceValidationException { try { - @SuppressWarnings("unused") final boolean val = (boolean) this.up.getValue(); } catch (final ClassCastException e) { - throw new PreferenceValidationException("Given value is not a boolean"); + throw new PreferenceValidationException("Given value is not a boolean", e); } } @@ -85,14 +85,14 @@ private void validate(final RangeSetting setting) throws PreferenceValidationExc // check if types match Number num; - final Double val; + double val; try { num = (Number) this.up.getValue(); val = num.doubleValue(); } catch (final ClassCastException e) { throw new PreferenceValidationException( String.format("Given value is not of type double but %s", - this.up.getValue().getClass().toString())); + this.up.getValue().getClass().toString()), e); } // Types match, check if value lies within min an max diff --git a/settings-service/src/test/java/net/explorviz/settings/services/SettingsRepositoryTest.java b/settings-service/src/test/java/net/explorviz/settings/services/SettingsRepositoryTest.java index eb7bb191..0eccd971 100644 --- a/settings-service/src/test/java/net/explorviz/settings/services/SettingsRepositoryTest.java +++ b/settings-service/src/test/java/net/explorviz/settings/services/SettingsRepositoryTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; + import com.mongodb.WriteResult; import java.util.ArrayList; import java.util.Arrays; @@ -30,9 +31,12 @@ import xyz.morphia.Key; import xyz.morphia.query.Query; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + /** * Unit tests for {@link SettingsRepository}. - * */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -104,11 +108,10 @@ public void testFindById() { @Test public void testRemove() { - when(this.ds.delete(RangeSetting.class, this.flagSettings.get(0).getId())).then(i -> { - return new WriteResult(0, false, null); - }); + when(this.ds.delete(RangeSetting.class, this.flagSettings.get(0).getId())) + .then(i -> new WriteResult(0, false, null)); when(this.ds.delete(FlagSetting.class, this.flagSettings.get(0).getId())).then(i -> { - SettingsRepositoryTest.this.flagSettings.remove(0); + this.flagSettings.remove(0); return new WriteResult(1, false, null); }); diff --git a/settings-service/src/test/java/net/explorviz/settings/services/UserPreferenceRepositoryTest.java b/settings-service/src/test/java/net/explorviz/settings/services/UserPreferenceRepositoryTest.java index f9eadaac..49b7db99 100644 --- a/settings-service/src/test/java/net/explorviz/settings/services/UserPreferenceRepositoryTest.java +++ b/settings-service/src/test/java/net/explorviz/settings/services/UserPreferenceRepositoryTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; + import com.mongodb.WriteResult; import java.util.ArrayList; import java.util.Arrays; @@ -26,12 +27,14 @@ import xyz.morphia.Key; import xyz.morphia.query.Query; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + /** * Unit test for {@link UserPreferenceRepository}. - * - * */ @ExtendWith(MockitoExtension.class) +@SuppressWarnings({"PMD.AvoidDuplicateLiterals"}) public class UserPreferenceRepositoryTest { @Mock diff --git a/settings-service/src/test/java/net/explorviz/settings/services/validation/PreferenceValidatorTest.java b/settings-service/src/test/java/net/explorviz/settings/services/validation/PreferenceValidatorTest.java index 55529cb0..ce7d7e5a 100644 --- a/settings-service/src/test/java/net/explorviz/settings/services/validation/PreferenceValidatorTest.java +++ b/settings-service/src/test/java/net/explorviz/settings/services/validation/PreferenceValidatorTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; + import net.explorviz.settings.model.FlagSetting; import net.explorviz.settings.model.RangeSetting; import net.explorviz.settings.model.UserPreference; @@ -60,8 +61,7 @@ public void testRangeValid() { try { this.validator.validate(this.range); } catch (final PreferenceValidationException e) { - e.printStackTrace(); - fail(ERROR); + fail(e); } } diff --git a/settings-service/src/test/java/net/explorviz/settings/testutils/TestDatasourceFactory.java b/settings-service/src/test/java/net/explorviz/settings/testutils/TestDatasourceFactory.java deleted file mode 100644 index 889b44ff..00000000 --- a/settings-service/src/test/java/net/explorviz/settings/testutils/TestDatasourceFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.explorviz.settings.testutils; - -import com.mongodb.MongoClient; -import net.explorviz.security.user.User; -import net.explorviz.shared.config.annotations.Config; -import org.glassfish.hk2.api.Factory; -import xyz.morphia.Datastore; -import xyz.morphia.Morphia; - - -public class TestDatasourceFactory implements Factory { - - // @Config("mongo.port") - // private String port; - - private final Datastore datastore; - - @Config("mongo.host") - @Config("mongo.port") - public TestDatasourceFactory(final String host, final String port) { - - final Morphia morphia = new Morphia(); - - // Map the model classes - morphia.map(User.class); - - this.datastore = morphia.createDatastore(new MongoClient(host + ":" + port), "explorviz_test"); - this.datastore.ensureIndexes(); - } - - @Override - public Datastore provide() { - return this.datastore; - } - - @Override - public void dispose(final Datastore instance) { - // nothing to do - } - -} diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/BatchRequest.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/BatchRequest.java index 4f4bf8fd..97a3e435 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/BatchRequest.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/BatchRequest.java @@ -2,6 +2,7 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; + import io.restassured.http.Header; import java.io.IOException; import java.util.ArrayList; @@ -13,32 +14,41 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import net.explorviz.security.model.UserBatchRequest; -import net.explorviz.security.server.resources.BatchRequestResource; import net.explorviz.security.server.resources.test.helper.AuthorizationHelper; -import net.explorviz.security.server.resources.test.helper.JsonAPIListMapper; -import net.explorviz.security.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.security.server.resources.test.helper.JsonApiListMapper; +import net.explorviz.security.server.resources.test.helper.JsonApiMapper; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import net.explorviz.security.server.resources.test.helper.UsersHelper; -import net.explorviz.settings.model.UserPreference; +import net.explorviz.security.services.BatchService; import net.explorviz.security.user.User; +import net.explorviz.settings.model.UserPreference; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests user batch requests. + */ public class BatchRequest { private static final String BATCH_URL = "http://localhost:8090/v1/userbatch"; private static final String USER_URL = "http://localhost:8090/v1/users/"; private static final String PREF_URL = "http://localhost:8090/v1/preferences?filter[user]={uid}"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; + private static final String PW_CHARSET = "abcdefghijklmnopqrstuvwxyz"; + private static final String ROLE_USER = "user"; private static String adminToken; - private static String normieToken; - private Header authHeaderAdmin; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + /** @@ -48,36 +58,35 @@ public class BatchRequest { * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); - normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); } @Test - void createValid() { + public void createValid() { final int count = 10; final List passwords = IntStream.range(0, count) - .mapToObj(i -> RandomStringUtils.random(5, "abcdefghijklmnopqrstuvwxyz")) + .mapToObj(i -> RandomStringUtils.random(5, PW_CHARSET)) .collect(Collectors.toList()); - final List roles = new ArrayList<>(Arrays.asList("user")); + final List roles = new ArrayList<>(Arrays.asList(ROLE_USER)); final UserBatchRequest ubr = new UserBatchRequest("test", count, passwords, roles, null); final UserBatchRequest retrieved = given().header(this.authHeaderAdmin) - .body(ubr, new JsonAPIMapper<>(UserBatchRequest.class)) + .body(ubr, new JsonApiMapper<>(UserBatchRequest.class)) .contentType(MEDIA_TYPE) .when() .post(BATCH_URL) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .extract() .body() - .as(UserBatchRequest.class, new JsonAPIMapper<>(UserBatchRequest.class)); + .as(UserBatchRequest.class, new JsonApiMapper<>(UserBatchRequest.class)); Assertions.assertEquals(count, retrieved.getUsers().size()); @@ -91,26 +100,26 @@ void createValid() { @Test @SuppressWarnings("unchecked") - void filterByBatchId() { + public void filterByBatchId() { final int count = 10; final List passwords = IntStream.range(0, count) - .mapToObj(i -> RandomStringUtils.random(5, "abcdefghijklmnopqrstuvwxyz")) + .mapToObj(i -> RandomStringUtils.random(5, PW_CHARSET)) .collect(Collectors.toList()); - final List roles = new ArrayList<>(Arrays.asList("user")); + final List roles = new ArrayList<>(Arrays.asList(ROLE_USER)); final UserBatchRequest ubr = new UserBatchRequest("test", count, passwords, roles, null); // Create the batch final String bid = given().header(this.authHeaderAdmin) - .body(ubr, new JsonAPIMapper<>(UserBatchRequest.class)) + .body(ubr, new JsonApiMapper<>(UserBatchRequest.class)) .contentType(MEDIA_TYPE) .when() .post(BATCH_URL) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .extract() .body() - .as(UserBatchRequest.class, new JsonAPIMapper<>(UserBatchRequest.class)) + .as(UserBatchRequest.class, new JsonApiMapper<>(UserBatchRequest.class)) .getUsers() .get(0) .getBatchId(); @@ -122,11 +131,11 @@ void filterByBatchId() { .when() .get(USER_URL) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(count)) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(User.class)); + .as(List.class, new JsonApiListMapper<>(User.class)); // Delete the just created users retrieved.stream().map(User::getId).forEach(i -> UsersHelper.getInstance().deleteUserById(i)); @@ -134,12 +143,12 @@ void filterByBatchId() { @SuppressWarnings("unchecked") @Test - void validWithPrefs() { + public void validWithPrefs() { final int count = 10; final List passwords = IntStream.range(0, count) - .mapToObj(i -> RandomStringUtils.random(5, "abcdefghijklmnopqrstuvwxyz")) + .mapToObj(i -> RandomStringUtils.random(5, PW_CHARSET)) .collect(Collectors.toList()); - final List roles = new ArrayList<>(Arrays.asList("user")); + final List roles = new ArrayList<>(Arrays.asList(ROLE_USER)); final float appVizTransparencyIntensity = 0.5f; final boolean showFpsCounter = true; @@ -150,15 +159,15 @@ void validWithPrefs() { // Create batch and retrieve the ids of the created users final List retrievedUids = given().header(this.authHeaderAdmin) - .body(ubr, new JsonAPIMapper<>(UserBatchRequest.class)) + .body(ubr, new JsonApiMapper<>(UserBatchRequest.class)) .contentType(MEDIA_TYPE) .when() .post(BATCH_URL) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .extract() .body() - .as(UserBatchRequest.class, new JsonAPIMapper<>(UserBatchRequest.class)) + .as(UserBatchRequest.class, new JsonApiMapper<>(UserBatchRequest.class)) .getUsers() .stream() .map(User::getId) @@ -172,10 +181,10 @@ void validWithPrefs() { .when() .get(url) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(UserPreference.class)); + .as(List.class, new JsonApiListMapper<>(UserPreference.class)); // Test if both prefs are present and the values are correct final double appVizPref = (double) retrievedPrefs.stream() @@ -199,51 +208,51 @@ void validWithPrefs() { @Test - void invalidPasswordsLength() { + public void invalidPasswordsLength() { final int count = 10; final List passwords = IntStream.range(0, count - 1) - .mapToObj(i -> RandomStringUtils.random(5, "abcdefghijklmnopqrstuvwxyz")) + .mapToObj(i -> RandomStringUtils.random(5, PW_CHARSET)) .collect(Collectors.toList()); - final List roles = new ArrayList<>(Arrays.asList("user")); + final List roles = new ArrayList<>(Arrays.asList(ROLE_USER)); final UserBatchRequest ubr = new UserBatchRequest("test", count, passwords, roles, null); given().header(this.authHeaderAdmin) - .body(ubr, new JsonAPIMapper<>(UserBatchRequest.class)) + .body(ubr, new JsonApiMapper<>(UserBatchRequest.class)) .contentType(MEDIA_TYPE) .when() .post(BATCH_URL) .then() - .statusCode(400); + .statusCode(StatusCodes.STATUS_BAD_REQUEST); // TODO: How to test that no users were actually created? } @Test - void countLimit() { - final int count = BatchRequestResource.MAX_COUNT + 1; + public void countLimit() { + final int count = BatchService.MAX_COUNT + 1; final List passwords = IntStream.range(0, count) - .mapToObj(i -> RandomStringUtils.random(5, "abcdefghijklmnopqrstuvwxyz")) + .mapToObj(i -> RandomStringUtils.random(5, PW_CHARSET)) .collect(Collectors.toList()); - final List roles = new ArrayList<>(Arrays.asList("user")); + final List roles = new ArrayList<>(Arrays.asList(ROLE_USER)); final UserBatchRequest ubr = new UserBatchRequest("test", count, passwords, roles, null); given().header(this.authHeaderAdmin) - .body(ubr, new JsonAPIMapper<>(UserBatchRequest.class)) + .body(ubr, new JsonApiMapper<>(UserBatchRequest.class)) .contentType(MEDIA_TYPE) .when() .post(BATCH_URL) .then() - .statusCode(400); + .statusCode(StatusCodes.STATUS_BAD_REQUEST); // TODO: How to test that no users were actually created? } @Test - void createExistingUser() { + public void createExistingUser() { final String prefix = "test"; final int count = 5; final Optional u = UsersHelper.getInstance().createUser(prefix + "-3", "pass", null); @@ -251,19 +260,19 @@ void createExistingUser() { Assertions.fail(); } final List passwords = IntStream.range(0, count) - .mapToObj(i -> RandomStringUtils.random(5, "abcdefghijklmnopqrstuvwxyz")) + .mapToObj(i -> RandomStringUtils.random(5, PW_CHARSET)) .collect(Collectors.toList()); - final List roles = new ArrayList<>(Arrays.asList("user")); + final List roles = new ArrayList<>(Arrays.asList(ROLE_USER)); final UserBatchRequest ubr = new UserBatchRequest("test", count, passwords, roles, null); given().header(this.authHeaderAdmin) - .body(ubr, new JsonAPIMapper<>(UserBatchRequest.class)) + .body(ubr, new JsonApiMapper<>(UserBatchRequest.class)) .contentType(MEDIA_TYPE) .when() .post(BATCH_URL) .then() - .statusCode(400); + .statusCode(StatusCodes.STATUS_BAD_REQUEST); // TODO: How to test that no users were actually created? diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/Login.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/Login.java index 9e44969f..5235cfb4 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/Login.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/Login.java @@ -1,11 +1,20 @@ package net.explorviz.security.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.mapper.ObjectMapperType; import net.explorviz.security.model.UserCredentials; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests the login/authentication. + */ public class Login { // This is bad since other test rely on this @@ -16,25 +25,25 @@ public class Login { @Test - void testValidLogin() { + public void testValidLogin() { given().contentType("application/json") .body(new UserCredentials(ADMIN_NAME, ADMIN_PW), ObjectMapperType.JACKSON_2) .when() .post(AUTH_URL) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.attributes", Matchers.hasKey("token")); } @Test - void testInvalidLogin() { + public void testInvalidLogin() { given().contentType("application/json") .body(new UserCredentials(ADMIN_NAME, "invalidpw"), ObjectMapperType.JACKSON_2) .when() .post(AUTH_URL) .then() - .statusCode(403); + .statusCode(StatusCodes.STATUS_FORBIDDEN); } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/RoleRetrieval.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/RoleRetrieval.java index ab285e3b..224f1996 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/RoleRetrieval.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/RoleRetrieval.java @@ -2,11 +2,12 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; + import io.restassured.http.Header; -import java.io.IOException; import java.util.List; import net.explorviz.security.server.resources.test.helper.AuthorizationHelper; -import net.explorviz.security.server.resources.test.helper.JsonAPIListMapper; +import net.explorviz.security.server.resources.test.helper.JsonApiListMapper; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import net.explorviz.security.user.Role; import org.junit.Assert; import org.junit.jupiter.api.BeforeAll; @@ -14,13 +15,16 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + +/** + * Tests the retrieval of available roles. + */ public class RoleRetrieval { private static final String ROLE_URL = "http://localhost:8090/v1/roles"; - private static final int HTTP_OK_CODE = 200; - private static final int HTTP_FORBIDDEN_CODE = 401; - // Currently only two roles exist, namely 'user' and 'admin' // It's not possible to add more roles @@ -35,16 +39,15 @@ public class RoleRetrieval { * Retrieves token for both an admin and an unprivileged user ("normie"). The default admin is * used for the former, a normie is created. * - * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); // NOCS this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @@ -60,11 +63,11 @@ public void testGetAllRolesAsAdmin() { .when() .get(ROLE_URL) .then() - .statusCode(HTTP_OK_CODE) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(2)) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(Role.class)); + .as(List.class, new JsonApiListMapper<>(Role.class)); Assert.assertEquals(actualRoles, retrieved); } @@ -76,7 +79,7 @@ public void testGetAllRolesAsNormie() { // NOPMD .when() .get(ROLE_URL) .then() - .statusCode(HTTP_FORBIDDEN_CODE); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); } } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserCreation.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserCreation.java index edf560eb..add623c4 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserCreation.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserCreation.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; + import io.restassured.http.ContentType; import io.restassured.http.Header; import java.io.IOException; @@ -11,19 +12,23 @@ import java.util.List; import net.explorviz.security.model.UserCredentials; import net.explorviz.security.server.resources.test.helper.AuthorizationHelper; -import net.explorviz.security.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.security.server.resources.test.helper.JsonApiMapper; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import net.explorviz.security.server.resources.test.helper.UserSerializationHelper; -import net.explorviz.security.user.User; import net.explorviz.security.user.Role; +import net.explorviz.security.user.User; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + class UserCreation { - private final static String AUTH_ROUTE = "http://localhost:8090/v1/tokens"; - private final static String BASE_URI = "http://localhost:8090/v1/"; + private static final String AUTH_ROUTE = "http://localhost:8090/v1/tokens"; + private static final String BASE_URI = "http://localhost:8090/v1/"; private static final String MEDIA_TYPE = "application/vnd.api+json"; @@ -39,16 +44,15 @@ class UserCreation { * Retrieves token for both an admin and an unprivileged user ("normie"). The default admin is * used for the former, a normie is created. * - * @throws IOException if serialization fails */ @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @@ -67,7 +71,7 @@ public void createValidWithoutRoles() throws IOException { .when() .post(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("$", hasKey("data")) .body("data.attributes.username", is(name)) .body("data", not(hasKey("relationship"))); @@ -88,7 +92,7 @@ public void createValidAdmin() throws IOException { .when() .post(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("$", hasKey("data")) .body("data.attributes.username", is(name)) .body("data.attributes.roles", is(roles)); @@ -115,7 +119,7 @@ public void loginWithCreatedUser() throws IOException { .when() .post(AUTH_ROUTE) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("$", hasKey("data")) .body("data.attributes", hasKey("token")); @@ -124,43 +128,43 @@ public void loginWithCreatedUser() throws IOException { @Test @DisplayName("Create user without password") - void createUserWithNoPassword() { + public void createUserWithNoPassword() { final User u = new User(null, "name", null, null); - given().body(u, new JsonAPIMapper<>(User.class)) + given().body(u, new JsonApiMapper<>(User.class)) .contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) .when() .post(BASE_URI + "users/") .then() - .statusCode(400); + .statusCode(StatusCodes.STATUS_BAD_REQUEST); } @Test @DisplayName("Create user without token.") - void createUserUnauthenticated() { + public void createUserUnauthenticated() { final User u = new User(null, "name", null, null); - given().body(u, new JsonAPIMapper<>(User.class)) + given().body(u, new JsonApiMapper<>(User.class)) .contentType(MEDIA_TYPE) .when() .post(BASE_URI + "users/") .then() - .statusCode(401); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); } @Test @DisplayName("Create user unauthenticated.") - void createUserAsNormie() { + public void createUserAsNormie() { final User u = new User(null, "name", null, null); - given().body(u, new JsonAPIMapper<>(User.class)) + given().body(u, new JsonApiMapper<>(User.class)) .contentType(MEDIA_TYPE) .header(this.authHeaderNormie) .when() .post(BASE_URI + "users/") .then() - .statusCode(401); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); } } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserDeletion.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserDeletion.java index 3696944e..c9412076 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserDeletion.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserDeletion.java @@ -1,10 +1,12 @@ package net.explorviz.security.server.resources.test; import static io.restassured.RestAssured.given; + import io.restassured.http.Header; import java.io.IOException; import java.util.Optional; import net.explorviz.security.server.resources.test.helper.AuthorizationHelper; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import net.explorviz.security.server.resources.test.helper.UsersHelper; import net.explorviz.security.user.User; import org.junit.jupiter.api.Assertions; @@ -13,9 +15,16 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + +/** + * Tests deletion of users. + */ public class UserDeletion { - private final static String BASE_URI = "http://localhost:8090/v1/"; + private static final String BASE_URI = "http://localhost:8090/v1/"; private static String adminToken; @@ -25,13 +34,13 @@ public class UserDeletion { private Header authHeaderNormie; @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() throws IOException { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } @@ -39,7 +48,7 @@ void setUp() { @Test @DisplayName("Delete a User") - void deleteUser() { + public void deleteUser() { final Optional deleteMe = UsersHelper.getInstance().createUser("deleteme", "pw", null); if (!deleteMe.isPresent()) { @@ -50,21 +59,21 @@ void deleteUser() { .when() .delete(BASE_URI + "users/" + deleteMe.get().getId()) .then() - .statusCode(204); + .statusCode(StatusCodes.STATUS_NO_CONTEND); given().header(this.authHeaderAdmin) .when() .get(BASE_URI + deleteMe.get().getId()) .then() - .statusCode(404); + .statusCode(StatusCodes.STATUS_NOT_FOUND); } @Test @DisplayName("Delete a User without privileges") - void deleteUserAsNormie() { + public void deleteUserAsNormie() { final Optional deleteMe = UsersHelper.getInstance().createUser("deleteme", "pw", null); - if (!deleteMe.isPresent()) { + if (deleteMe.isEmpty()) { Assertions.fail(); } @@ -72,14 +81,14 @@ void deleteUserAsNormie() { .when() .delete(BASE_URI + "users/" + deleteMe.get().getId()) .then() - .statusCode(401); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); UsersHelper.getInstance().deleteUserById(deleteMe.get().getId()); } @Test @DisplayName("Delete last admin") - void deleteLastAdmin() { + public void deleteLastAdmin() { // Check if there are other admins next to the default admin, and delete them UsersHelper.getInstance() @@ -96,7 +105,7 @@ void deleteLastAdmin() { .when() .delete(BASE_URI + "users/" + id) .then() - .statusCode(400); + .statusCode(StatusCodes.STATUS_BAD_REQUEST); } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserRetrieval.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserRetrieval.java index 0ea29836..c3f22582 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserRetrieval.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserRetrieval.java @@ -6,14 +6,15 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertTrue; + import io.restassured.http.Header; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import net.explorviz.security.server.resources.test.helper.AuthorizationHelper; -import net.explorviz.security.server.resources.test.helper.JsonAPIListMapper; +import net.explorviz.security.server.resources.test.helper.JsonApiListMapper; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import net.explorviz.security.server.resources.test.helper.UsersHelper; import net.explorviz.security.services.exceptions.UserCrudException; import net.explorviz.security.user.User; @@ -23,11 +24,25 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + +/** + * Tests retrieval of users. + */ public class UserRetrieval { - private final static String BASE_URI = "http://localhost:8090/v1/"; + private static final String BASE_URI = "http://localhost:8090/v1/"; private static final String MEDIA_TYPE = "application/vnd.api+json"; + private static final String LINKS_PATH = "links"; + private static final String PAGE_SIZE = "page[size]"; + private static final String PAGE_NUMBER = "page[number]"; + private static final String LINK_FIRST = "first"; + private static final String LINK_PREV = "prev"; + private static final String LINK_LAST = "last"; + private static final String LINK_NEXT = "next"; + private static final String FILTER_ROLES = "filter[roles]"; private static String adminToken; @@ -37,20 +52,20 @@ public class UserRetrieval { @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); } private List createUsers(final int count, final String prefix, final String password, - final List roles) { + final List roles) { final List users = new ArrayList<>(); for (int i = 0; i < count; i++) { final Optional created = @@ -72,7 +87,7 @@ public void tearDown() throws UserCrudException { @Test @DisplayName("Get all users in the database") - public void getAllAsAdmin() throws UserCrudException { + public void retrieveAllAsAdmin() throws UserCrudException { final List created = this.createUsers(5, "test", "pw", null); // All users must be >= 1 given().contentType(MEDIA_TYPE) @@ -80,7 +95,7 @@ public void getAllAsAdmin() throws UserCrudException { .when() .get(BASE_URI + "users") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("$", hasKey("data")) .body("data.size()", greaterThan(5)); this.deleteUsers(created); @@ -88,41 +103,41 @@ public void getAllAsAdmin() throws UserCrudException { @Test @DisplayName("Get all users in the database as normie") - public void getAllAsNormie() { + public void retrieveAllAsNormie() { // Should return 401 status code given().contentType(MEDIA_TYPE) .header(this.authHeaderNormie) .when() .get(BASE_URI + "users") .then() - .statusCode(401); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); } @Test @DisplayName("Get user by id as admin") - public void getSelfAsAdmin() { + public void retrieveSelfAsAdmin() { final String id = AuthorizationHelper.getAdmin().getId(); given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) .when() .get(BASE_URI + "users/" + id) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("$", hasKey("data")) .body("data.id", is(id)); } @Test @DisplayName("Get user by id as normie") - public void getUserAsNormie() { + public void retrieveUserAsNormie() { final String id = AuthorizationHelper.getNormie().getId(); given().contentType(MEDIA_TYPE) .header(this.authHeaderNormie) .when() .get(BASE_URI + "users/" + id) .then() - .statusCode(401); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); } @Test @@ -133,16 +148,16 @@ public void paginationFirstPage() { final int num = 0; given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) - .params("page[size]", size, "page[number]", num) + .params(PAGE_SIZE, size, PAGE_NUMBER, num) .when() .get(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(size)) - .body("links", hasKey("first")) - .body("links", hasKey("next")) - .body("links", hasKey("last")) - .body("links", not(hasKey("prev"))); + .body(LINKS_PATH, hasKey(LINK_FIRST)) + .body(LINKS_PATH, hasKey(LINK_NEXT)) + .body(LINKS_PATH, hasKey(LINK_LAST)) + .body(LINKS_PATH, not(hasKey(LINK_PREV))); this.deleteUsers(users); } @@ -154,16 +169,16 @@ public void paginationLastPage() { final int num = UsersHelper.getInstance().count() - 1; given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) - .params("page[size]", size, "page[number]", num) + .params(PAGE_SIZE, size, PAGE_NUMBER, num) .when() .get(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(size)) - .body("links", hasKey("first")) - .body("links", hasKey("prev")) - .body("links", hasKey("last")) - .body("links", not(hasKey("next"))); + .body(LINKS_PATH, hasKey(LINK_FIRST)) + .body(LINKS_PATH, hasKey(LINK_PREV)) + .body(LINKS_PATH, hasKey(LINK_LAST)) + .body(LINKS_PATH, not(hasKey(LINK_NEXT))); this.deleteUsers(users); } @@ -175,16 +190,16 @@ public void paginationEmptyPage() { final int num = UsersHelper.getInstance().count(); given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) - .params("page[size]", size, "page[number]", num) + .params(PAGE_SIZE, size, PAGE_NUMBER, num) .when() .get(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(0)) - .body("links", hasKey("first")) - .body("links", hasKey("prev")) - .body("links", hasKey("last")) - .body("links", not(hasKey("next"))); + .body(LINKS_PATH, hasKey(LINK_FIRST)) + .body(LINKS_PATH, hasKey(LINK_PREV)) + .body(LINKS_PATH, hasKey(LINK_LAST)) + .body(LINKS_PATH, not(hasKey(LINK_NEXT))); this.deleteUsers(users); } @@ -196,16 +211,16 @@ public void paginationMiddlePage() { final int num = UsersHelper.getInstance().count() / 2; given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) - .params("page[size]", size, "page[number]", num) + .params(PAGE_SIZE, size, PAGE_NUMBER, num) .when() .get(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(size)) - .body("links", hasKey("first")) - .body("links", hasKey("prev")) - .body("links", hasKey("last")) - .body("links", hasKey("next")); + .body(LINKS_PATH, hasKey(LINK_FIRST)) + .body(LINKS_PATH, hasKey(LINK_PREV)) + .body(LINKS_PATH, hasKey(LINK_LAST)) + .body(LINKS_PATH, hasKey(LINK_NEXT)); this.deleteUsers(users); } @@ -213,22 +228,22 @@ public void paginationMiddlePage() { @Test @DisplayName("Filter by Role") @SuppressWarnings("unchecked") - public void FilterByRole() { + public void filterByRole() { final String adminRole = "admin"; final List roles = new ArrayList<>(Arrays.asList(adminRole)); final List users = this.createUsers(5, "test", "pw", roles); final List returned = given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) - .params("filter[roles]", adminRole) + .params(FILTER_ROLES, adminRole) .when() .get(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", greaterThan(5)) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(User.class)); + .as(List.class, new JsonApiListMapper<>(User.class)); returned @@ -241,7 +256,7 @@ public void FilterByRole() { @Test @DisplayName("Filter by Role and Paginate") @SuppressWarnings("unchecked") - public void FilterByRoleAndPaginate() { + public void filterByRoleAndPaginate() { final String adminRole = "admin"; final List roles = new ArrayList<>(Arrays.asList(adminRole)); final List users = this.createUsers(5, "test", "pw", roles); @@ -249,15 +264,15 @@ public void FilterByRoleAndPaginate() { final int num = UsersHelper.getInstance().count() / 2; final List returned = given().contentType(MEDIA_TYPE) .header(this.authHeaderAdmin) - .params("filter[roles]", adminRole, "page[size]", size, "page[number]", num) + .params(FILTER_ROLES, adminRole, PAGE_SIZE, size, PAGE_NUMBER, num) .when() .get(BASE_URI + "users/") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.size()", is(size)) .extract() .body() - .as(List.class, new JsonAPIListMapper<>(User.class)); + .as(List.class, new JsonApiListMapper<>(User.class)); returned .forEach(u -> assertTrue(u.getRoles().stream().anyMatch(r -> r.contentEquals(adminRole)))); diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserUpdate.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserUpdate.java index 4c917d85..bfdabc68 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserUpdate.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/UserUpdate.java @@ -3,16 +3,18 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.hasKey; import static org.junit.jupiter.api.Assertions.assertTrue; + import io.restassured.http.Header; import io.restassured.mapper.ObjectMapperType; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; import net.explorviz.security.model.UserCredentials; import net.explorviz.security.server.resources.test.helper.AuthorizationHelper; -import net.explorviz.security.server.resources.test.helper.JsonAPIMapper; +import net.explorviz.security.server.resources.test.helper.JsonApiMapper; +import net.explorviz.security.server.resources.test.helper.StatusCodes; import net.explorviz.security.server.resources.test.helper.UserSerializationHelper; import net.explorviz.security.server.resources.test.helper.UsersHelper; import net.explorviz.security.user.User; @@ -21,14 +23,22 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// CHECKSTYLE.OFF: MagicNumberCheck +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + +/** + * Tests user updates. + */ public class UserUpdate { - private final static String BASE_URI = "http://localhost:8090/v1/"; + private static final String BASE_URI = "http://localhost:8090/v1/"; + private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json"; + private static final String MEDIA_TYPE = "application/vnd.api+json"; - private final String USER_PW = "pass"; - private final String USER_NAME = "user"; + private static final String USER_PW = "password"; + private static final String USER_NAME = "user"; private static String adminToken; @@ -37,24 +47,24 @@ public class UserUpdate { private Header authHeaderAdmin; private Header authHeaderNormie; - private static final String MEDIA_TYPE = "application/vnd.api+json"; + private User theUser; private String userUri; @BeforeAll - static void setUpAll() throws IOException { + public static void setUpAll() { adminToken = AuthorizationHelper.getAdminToken(); normieToken = AuthorizationHelper.getNormieToken(); } @BeforeEach - void setUp() { + public void setUp() { this.authHeaderAdmin = new Header("authorization", "Bearer " + adminToken); this.authHeaderNormie = new Header("authorization", "Bearer " + normieToken); final Optional opt = - UsersHelper.getInstance().createUser(this.USER_NAME, this.USER_PW, null); + UsersHelper.getInstance().createUser(USER_NAME, USER_PW, null); if (opt.isPresent()) { this.theUser = opt.get(); @@ -66,15 +76,15 @@ void setUp() { } @AfterEach - void tearDown() { + public void tearDown() { UsersHelper.getInstance().deleteUserById(this.theUser.getId()); } @Test - void changePassword() throws IOException { + public void changePassword() throws IOException { final String newpw = "newpw"; - final User changeTo = new User(null, this.USER_NAME, newpw, null); + final User changeTo = new User(null, null, newpw, null); // Perform patch given().header(this.authHeaderAdmin) @@ -83,26 +93,27 @@ void changePassword() throws IOException { .when() .patch(this.userUri) .then() - .statusCode(200); + .statusCode(StatusCodes.STATUS_OK); + // Try to login with new pw given().body(new UserCredentials(this.USER_NAME, newpw), ObjectMapperType.JACKSON_2) - .contentType("application/json") + .contentType(MEDIA_TYPE_APPLICATION_JSON) .when() .post(BASE_URI + "tokens") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.attributes", hasKey("token")); } @Test - void changeName() throws IOException { + public void changeName() throws IOException { final String newname = "newname"; - final User changeTo = new User(null, newname, this.USER_PW, null); + final User changeTo = new User(null, newname, USER_PW, null); // Perform patch given().header(this.authHeaderAdmin) @@ -111,21 +122,24 @@ void changeName() throws IOException { .when() .patch(this.userUri) .then() - .statusCode(200); + .statusCode(StatusCodes.STATUS_OK) + .extract().body() + .as(User.class, new JsonApiMapper<>(User.class)); + // Try to login with new name - given().body(new UserCredentials(newname, this.USER_PW), ObjectMapperType.JACKSON_2) - .contentType("application/json") + given().body(new UserCredentials(newname, USER_PW), ObjectMapperType.JACKSON_2) + .contentType(MEDIA_TYPE_APPLICATION_JSON) .when() .post(BASE_URI + "tokens") .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .body("data.attributes", hasKey("token")); } @Test - void updateAsNormie() throws IOException { + public void updateAsNormie() throws IOException { final String newname = "newname"; final User changeTo = new User(null, newname, this.USER_PW, null); @@ -136,14 +150,14 @@ void updateAsNormie() throws IOException { .when() .patch(this.userUri) .then() - .statusCode(401); + .statusCode(StatusCodes.STATUS_UNAUTHORIZED); } @Test - void addRole() throws IOException { - final List adminRole = new ArrayList<>(Arrays.asList("admin")); - final User changeTo = new User(null, this.USER_NAME, this.USER_PW, adminRole); + public void addRole() throws IOException { + final List adminRole = new ArrayList<>(Collections.singletonList("admin")); + final User changeTo = new User(null, USER_NAME, USER_PW, adminRole); // Perform patch final User updated = given().header(this.authHeaderAdmin) @@ -152,19 +166,19 @@ void addRole() throws IOException { .when() .patch(this.userUri) .then() - .statusCode(200) + .statusCode(StatusCodes.STATUS_OK) .extract() .body() - .as(User.class, new JsonAPIMapper<>(User.class)); + .as(User.class, new JsonApiMapper<>(User.class)); - assertTrue(updated.getRoles().stream().map(r -> r).anyMatch(d -> d.contentEquals("admin"))); + assertTrue(updated.getRoles().stream().anyMatch(d -> d.contentEquals("admin"))); } @Test - void updateId() throws IOException { - final User changeTo = new User("someid", this.USER_NAME, this.USER_PW, null); + public void updateId() throws IOException { + final User changeTo = new User("someid", USER_NAME, USER_PW, null); // Perform patch given().header(this.authHeaderAdmin) @@ -173,7 +187,7 @@ void updateId() throws IOException { .when() .patch(this.userUri) .then() - .statusCode(400); + .statusCode(StatusCodes.STATUS_BAD_REQUEST); } } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/AuthorizationHelper.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/AuthorizationHelper.java index 325a091e..44d2ac2c 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/AuthorizationHelper.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/AuthorizationHelper.java @@ -1,26 +1,31 @@ package net.explorviz.security.server.resources.test.helper; import static io.restassured.RestAssured.given; + import com.github.jasminb.jsonapi.exceptions.ResourceParseException; import io.restassured.mapper.ObjectMapperType; import java.util.Optional; import net.explorviz.security.model.UserCredentials; import net.explorviz.security.user.User; -public class AuthorizationHelper { +/** + * Utility class to perform the login process in other test cases. + */ +public final class AuthorizationHelper { + + private static final String AUTH_URL = "http://localhost:8090/v1/tokens/"; private static final String ADMIN_NAME = "admin"; private static final String NORMIE_NAME = "normie"; private static final String ADMIN_PW = "password"; - private static final String NORMIE_PW = "password"; + private static final String NORMIE_PW = ADMIN_PW; - private static String normieToken = null; - private static String adminToken = null; + private static User admin; + private static User normie; - private static User admin = null; - private static User normie = null; + private AuthorizationHelper(){/* Utility */} private static Optional login(final String name, final String password) { @@ -29,7 +34,7 @@ private static Optional login(final String name, final String password) { .body(new UserCredentials(name, password), ObjectMapperType.JACKSON_2) .when() .post(AUTH_URL) - .as(User.class, new JsonAPIMapper<>(User.class)); + .as(User.class, new JsonApiMapper<>(User.class)); return Optional.of(u); } catch (final ResourceParseException ex) { return Optional.empty(); @@ -45,6 +50,9 @@ public static String getAdminToken() { return getAdmin().getToken(); } + /** + * Return a {@link User} without no roles, i.e., without any elevated permissions. + */ public static User getNormie() { final Optional normie = login(NORMIE_NAME, NORMIE_PW); if (AuthorizationHelper.normie == null) { @@ -53,9 +61,9 @@ public static User getNormie() { } else { // Not existent, create and try again // Will fail if normie user exists with another password - final Optional created_normie = + final Optional createdNormie = UsersHelper.getInstance().createUser(NORMIE_NAME, NORMIE_PW, null); - if (created_normie.isPresent()) { + if (createdNormie.isPresent()) { return getNormie(); } else { throw new IllegalStateException("Can no login as normie, does no exist"); @@ -66,6 +74,9 @@ public static User getNormie() { return AuthorizationHelper.normie; } + /** + * Returns a {@link User} with the admin role assigned. + */ public static User getAdmin() { if (AuthorizationHelper.admin == null) { final Optional admin = login(ADMIN_NAME, ADMIN_PW); diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonAPIListMapper.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonApiListMapper.java similarity index 79% rename from user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonAPIListMapper.java rename to user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonApiListMapper.java index 68cad64f..b67eb2dd 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonAPIListMapper.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonApiListMapper.java @@ -7,16 +7,24 @@ import io.restassured.mapper.ObjectMapperDeserializationContext; import io.restassured.mapper.ObjectMapperSerializationContext; import java.util.List; -import net.explorviz.settings.model.UserPreference; -import net.explorviz.security.user.User; import net.explorviz.security.user.Role; +import net.explorviz.security.user.User; +import net.explorviz.settings.model.UserPreference; -public class JsonAPIListMapper implements ObjectMapper { +/** + * Custom mapper to de/serialize JSON:API collections to lists. + * @param Type to de/serialize + */ +public class JsonApiListMapper implements ObjectMapper { private final ResourceConverter converter; private final Class cls; - public JsonAPIListMapper(final Class cls) { + /** + * Creates a new mapper. + * @param cls The class to de/serialize the list entries into. + */ + public JsonApiListMapper(final Class cls) { this.cls = cls; this.converter = new ResourceConverter(); this.converter.registerType(User.class); @@ -38,9 +46,7 @@ public String serialize(final ObjectMapperSerializationContext context) { try { final byte[] serialized = this.converter.writeDocumentCollection(doc); return new String(serialized); - } catch (final DocumentSerializationException e) { - e.printStackTrace(); - } + } catch (final DocumentSerializationException ignored) { } return null; } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonAPIMapper.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonApiMapper.java similarity index 71% rename from user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonAPIMapper.java rename to user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonApiMapper.java index 6d0b7778..8c639852 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonAPIMapper.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/JsonApiMapper.java @@ -8,15 +8,25 @@ import io.restassured.mapper.ObjectMapperDeserializationContext; import io.restassured.mapper.ObjectMapperSerializationContext; import net.explorviz.security.model.UserBatchRequest; -import net.explorviz.security.user.User; import net.explorviz.security.user.Role; +import net.explorviz.security.user.User; -public class JsonAPIMapper implements ObjectMapper { +/** + * Custom mapper to de/serialize JSON:API. + * + * @param Type to de/serialize + */ +public class JsonApiMapper implements ObjectMapper { private final ResourceConverter converter; private final Class cls; - public JsonAPIMapper(final Class cls) { + /** + * Creates a new mapper. + * + * @param cls The class to de/serialize. + */ + public JsonApiMapper(final Class cls) { this.cls = cls; this.converter = new ResourceConverter(); this.converter.registerType(User.class); @@ -27,9 +37,8 @@ public JsonAPIMapper(final Class cls) { @Override public T deserialize(final ObjectMapperDeserializationContext context) { - final T deserialized = - this.converter.readDocument(context.getDataToDeserialize().asByteArray(), this.cls).get(); - return deserialized; + return this.converter.readDocument(context.getDataToDeserialize().asByteArray(), this.cls) + .get(); } @Override @@ -38,19 +47,23 @@ public String serialize(final ObjectMapperSerializationContext context) { try { final byte[] serialized = this.converter.writeDocument(doc); return new String(serialized); - } catch (final DocumentSerializationException e) { - e.printStackTrace(); + } catch (final DocumentSerializationException ignored) { } return null; } + /** + * Wrapper to serialize an object. + * + * @param object the object to serialize + * @return JSON:API string representation of the given object + */ public String serializeRaw(final T object) { final JSONAPIDocument doc = new JSONAPIDocument<>(object); try { final byte[] serialized = this.converter.writeDocument(doc); return new String(serialized); - } catch (final DocumentSerializationException e) { - e.printStackTrace(); + } catch (final DocumentSerializationException ignored) { } return null; } diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/StatusCodes.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/StatusCodes.java new file mode 100644 index 00000000..a9348eef --- /dev/null +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/StatusCodes.java @@ -0,0 +1,13 @@ +package net.explorviz.security.server.resources.test.helper; + +/** + * Contains HTTP status codes constants. + */ +public class StatusCodes { + public static final int STATUS_OK = 200; + public static final int STATUS_BAD_REQUEST = 400; + public static final int STATUS_FORBIDDEN = 403; + public static final int STATUS_UNAUTHORIZED = 401; + public static final int STATUS_NO_CONTEND = 204; + public static final int STATUS_NOT_FOUND = 404; +} diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UserSerializationHelper.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UserSerializationHelper.java index d4e7abaa..b53df3c8 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UserSerializationHelper.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UserSerializationHelper.java @@ -6,17 +6,23 @@ import java.io.IOException; import net.explorviz.security.user.User; -public class UserSerializationHelper { +/** + * Helper class for serializing {@link User} objects with passwords. + * Using the default converter, the password is omitted. This class prevents this. + */ +public final class UserSerializationHelper { + + private UserSerializationHelper(){/* Utility */} /** - * Serializes a user WITH the password attribute, which otherwise is ignored by Jackson + * Serializes a user WITH the password attribute, which otherwise is ignored by Jackson. * * @param u the user * @return a JSON:API string representing the user * @throws IOException if the json is invalid */ public static String serialize(final User u) throws IOException { - final String serialized = new JsonAPIMapper<>(User.class).serializeRaw(u); + final String serialized = new JsonApiMapper<>(User.class).serializeRaw(u); // Password is ignored we need to add it manually into the JSON tree final ObjectMapper mapper = new ObjectMapper(); diff --git a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UsersHelper.java b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UsersHelper.java index daa6152e..13b77e76 100644 --- a/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UsersHelper.java +++ b/user-service/src/apiTest/java/net/explorviz/security/server/resources/test/helper/UsersHelper.java @@ -1,6 +1,7 @@ package net.explorviz.security.server.resources.test.helper; import static io.restassured.RestAssured.given; + import com.github.jasminb.jsonapi.exceptions.ResourceParseException; import io.restassured.http.Header; import java.io.IOException; @@ -14,41 +15,49 @@ * Helper class for manipulating users through HTTP. Represents a minimal client to the user API. * All requests are performed as the default admin. */ -public class UsersHelper { +public final class UsersHelper { private static final Logger LOGGER = LoggerFactory.getLogger(UsersHelper.class); private static final String MEDIA_TYPE = "application/vnd.api+json"; - private final static String USERS_URI = "http://localhost:8090/v1/users/"; + private static final String USERS_URI = "http://localhost:8090/v1/users/"; + + private static UsersHelper instance; //NOPMD + + private final Header auth; + + private UsersHelper() { + final String tok = AuthorizationHelper.getAdminToken(); + this.auth = new Header("authorization", "Bearer " + tok); + } + /** + * Returns the instance. + */ public static UsersHelper getInstance() { - if (instance == null) { + if (instance == null) { //NOPMD instance = new UsersHelper(); } return instance; } - private static UsersHelper instance = null; - private final Header auth; - private UsersHelper() { - final String tok = AuthorizationHelper.getAdminToken(); - this.auth = new Header("authorization", "Bearer " + tok); - } + + /** * Creates a new user by issuing a post request. * - * @param name name of the user + * @param name name of the user * @param password password of the user - * @param roles roles of the user + * @param roles roles of the user * @return An optional containing the created user as returned by the API or null if an error - * occured. + * occured. */ public Optional createUser(final String name, final String password, - final List roles) { + final List roles) { final User toCreate = new User(null, name, password, roles); try { @@ -57,7 +66,7 @@ public Optional createUser(final String name, final String password, .header(this.auth) .when() .post(USERS_URI) - .as(User.class, new JsonAPIMapper<>(User.class)); + .as(User.class, new JsonApiMapper<>(User.class)); return Optional.of(u); } catch (final IOException e) { return Optional.empty(); @@ -68,7 +77,7 @@ public Optional createUser(final String name, final String password, } /** - * Delete a user by id + * Delete a user by id. * * @param id if of the user to delete */ @@ -77,12 +86,15 @@ public void deleteUserById(final String id) { } + /** + * Returns all users. + */ public List getAll() { return given().contentType(MEDIA_TYPE) .header(this.auth) .when() .get(USERS_URI) - .as(List.class, new JsonAPIListMapper<>(User.class)); + .as(List.class, new JsonApiListMapper<>(User.class)); } public int count() { diff --git a/user-service/src/main/java/net/explorviz/security/model/UserBatchRequest.java b/user-service/src/main/java/net/explorviz/security/model/UserBatchRequest.java index dfd7b588..1d2f850e 100644 --- a/user-service/src/main/java/net/explorviz/security/model/UserBatchRequest.java +++ b/user-service/src/main/java/net/explorviz/security/model/UserBatchRequest.java @@ -23,7 +23,7 @@ public class UserBatchRequest { @Id - private String id; + private String id; // NOPMD private final String prefix; private final int count; diff --git a/user-service/src/main/java/net/explorviz/security/model/UserCredentials.java b/user-service/src/main/java/net/explorviz/security/model/UserCredentials.java index 9ad46673..cc4b34a2 100644 --- a/user-service/src/main/java/net/explorviz/security/model/UserCredentials.java +++ b/user-service/src/main/java/net/explorviz/security/model/UserCredentials.java @@ -13,7 +13,7 @@ public UserCredentials(final String username, final String password) { this.password = password; } - public UserCredentials() {} + public UserCredentials() {/* Jackson */} public String getUsername() { return this.username; diff --git a/user-service/src/main/java/net/explorviz/security/server/main/Application.java b/user-service/src/main/java/net/explorviz/security/server/main/Application.java index 3e1c33f3..cefca723 100644 --- a/user-service/src/main/java/net/explorviz/security/server/main/Application.java +++ b/user-service/src/main/java/net/explorviz/security/server/main/Application.java @@ -3,7 +3,12 @@ import net.explorviz.security.model.UserBatchRequest; import net.explorviz.security.server.providers.GenericJsonApiPaginationWriter; import net.explorviz.security.server.providers.UserJsonApiDeserializer; -import net.explorviz.security.server.resources.*; + +import net.explorviz.security.server.resources.BatchRequestResource; +import net.explorviz.security.server.resources.EntryPointResource; +import net.explorviz.security.server.resources.RoleResource; +import net.explorviz.security.server.resources.TokenResource; +import net.explorviz.security.server.resources.UserResource; import net.explorviz.security.user.Role; import net.explorviz.security.user.User; import net.explorviz.settings.model.UserPreference; diff --git a/user-service/src/main/java/net/explorviz/security/server/main/DependencyInjectionBinder.java b/user-service/src/main/java/net/explorviz/security/server/main/DependencyInjectionBinder.java index 146059a0..536a28ee 100644 --- a/user-service/src/main/java/net/explorviz/security/server/main/DependencyInjectionBinder.java +++ b/user-service/src/main/java/net/explorviz/security/server/main/DependencyInjectionBinder.java @@ -8,7 +8,7 @@ import net.explorviz.security.services.KafkaUserService; import net.explorviz.security.services.TokenService; import net.explorviz.security.services.UserService; -import net.explorviz.security.services.exceptions.UserValidationService; +import net.explorviz.security.services.UserValidationService; import net.explorviz.shared.common.idgen.IdGenerator; import net.explorviz.shared.common.injection.CommonDependencyInjectionBinder; import org.apache.kafka.clients.producer.KafkaProducer; diff --git a/user-service/src/main/java/net/explorviz/security/server/main/SetupApplicationListener.java b/user-service/src/main/java/net/explorviz/security/server/main/SetupApplicationListener.java index 534817e3..8051ca62 100644 --- a/user-service/src/main/java/net/explorviz/security/server/main/SetupApplicationListener.java +++ b/user-service/src/main/java/net/explorviz/security/server/main/SetupApplicationListener.java @@ -5,10 +5,10 @@ import javax.servlet.annotation.WebListener; import net.explorviz.security.services.UserService; import net.explorviz.security.services.exceptions.UserCrudException; +import net.explorviz.security.user.Role; import net.explorviz.security.user.User; import net.explorviz.security.util.PasswordStorage; import net.explorviz.security.util.PasswordStorage.CannotPerformOperationException; -import net.explorviz.security.user.Role; import org.glassfish.jersey.server.monitoring.ApplicationEvent; import org.glassfish.jersey.server.monitoring.ApplicationEvent.Type; import org.glassfish.jersey.server.monitoring.ApplicationEventListener; diff --git a/user-service/src/main/java/net/explorviz/security/server/providers/UserJsonApiDeserializer.java b/user-service/src/main/java/net/explorviz/security/server/providers/UserJsonApiDeserializer.java index c8e378b7..5faaf548 100644 --- a/user-service/src/main/java/net/explorviz/security/server/providers/UserJsonApiDeserializer.java +++ b/user-service/src/main/java/net/explorviz/security/server/providers/UserJsonApiDeserializer.java @@ -1,7 +1,6 @@ package net.explorviz.security.server.providers; import com.github.jasminb.jsonapi.ResourceConverter; -import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; @@ -12,6 +11,9 @@ import javax.ws.rs.ext.MessageBodyReader; import net.explorviz.security.user.User; +/** + * Serializer that parses JSON:API into {@link User} objects. + */ public class UserJsonApiDeserializer implements MessageBodyReader { private final ResourceConverter converter; @@ -32,10 +34,9 @@ public boolean isReadable(final Class type, final Type genericType, public User readFrom(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap httpHeaders, final InputStream entityStream) - throws IOException, WebApplicationException { + throws WebApplicationException { - final User user = this.converter.readDocument(entityStream, type).get(); - return user; + return this.converter.readDocument(entityStream, type).get(); } } diff --git a/user-service/src/main/java/net/explorviz/security/server/resources/BatchRequestResource.java b/user-service/src/main/java/net/explorviz/security/server/resources/BatchRequestResource.java index 8a7a1df0..a38a2c7a 100644 --- a/user-service/src/main/java/net/explorviz/security/server/resources/BatchRequestResource.java +++ b/user-service/src/main/java/net/explorviz/security/server/resources/BatchRequestResource.java @@ -35,7 +35,6 @@ /** * Resource that handle batch creation requests. - * */ @Tags(value = {@Tag(name = "Batch"), @Tag(name = "User")}) @SecurityRequirement(name = "token") @@ -43,11 +42,10 @@ @Path("v1/userbatch") public class BatchRequestResource { - private static final Logger LOGGER = LoggerFactory.getLogger(BatchRequestResource.class); - private static final String MEDIA_TYPE = "application/vnd.api+json"; - public static final int MAX_COUNT = 300; + private static final Logger LOGGER = LoggerFactory.getLogger(BatchRequestResource.class); + private static final String MEDIA_TYPE = "application/vnd.api+json"; private final BatchService bcs; @@ -68,39 +66,25 @@ public BatchRequestResource(final BatchService batchCreationService) { @RolesAllowed({Role.ADMIN_NAME}) @Operation(summary = "Create a batch of users with a single request") @ApiResponse(responseCode = "200", - description = "Contains all users created through this batch request. " - + "All users created by the same batch request have the same value" - + " for the attribute 'batchId'.", - content = @Content(mediaType = MEDIA_TYPE, - schema = @Schema(implementation = UserBatchRequest.class))) + description = "Contains all users created through this batch request. " + + "All users created by the same batch request have the same value" + + " for the attribute 'batchId'.", + content = @Content(mediaType = MEDIA_TYPE, + schema = @Schema(implementation = UserBatchRequest.class))) @ApiResponse(responseCode = "400", description = "Invalid request body or a user already exists. " + "In this case, the whole request is rolled back.") @RequestBody( description = "An object specifying the batch request. " + "The 'count' attribute denotes how many users to create." - + "It must be greater than 0 but smaller than " + MAX_COUNT + ". " + + "It must be greater than 0 but smaller than " + BatchService.MAX_COUNT + ". " + "The name of alle users start with the 'prefix' and end with an index " + "The prefix thus must not be empty." + "The size of the password list must match the amount of users to create. " + "The given preferences are valid for all users.", content = @Content(schema = @Schema(implementation = UserBatchRequest.class))) public UserBatchRequest batchCreate(@Context final HttpHeaders headers, - final UserBatchRequest batch) { + final UserBatchRequest batch) { try { - if (batch.getCount() > MAX_COUNT) { - throw new MalformedBatchRequestException("Count must be smaller than " + MAX_COUNT); - } - - if (batch.getCount() == 0) { - throw new MalformedBatchRequestException("Count must be bigger than 0"); - } - if (batch.getPrefix() == null || batch.getPrefix().isEmpty()) { - throw new MalformedBatchRequestException("Prefix can't be empty"); - } - if (batch.getPasswords() == null || batch.getPasswords().size() != batch.getCount()) { - throw new MalformedBatchRequestException("Passwords must match size of users to create"); - } - final List created = this.bcs.create(batch, headers.getHeaderString(HttpHeaders.AUTHORIZATION)); @@ -110,17 +94,22 @@ public UserBatchRequest batchCreate(@Context final HttpHeaders headers, } catch (final DuplicateUserException e) { throw new BadRequestException( - "At least one of the users to create already exists. No user was created"); + "At least one of the users to create already exists. No user was created", e); } catch (final MalformedBatchRequestException e) { LOGGER.error(e.getMessage()); - throw new BadRequestException(e.getMessage()); + throw new BadRequestException(e.getMessage(), e); } catch (final UserCrudException e) { LOGGER.error(e.getMessage()); - throw new InternalServerErrorException("No user created."); + throw new InternalServerErrorException("No user created.", e); } } + /** + * Deletes all users that were created through a batch creation request. + * + * @param batchid the id of the batch the users were created by + */ @DELETE @Path("/{batch_id}") public Response deleteBatch(@PathParam("batch_id") final String batchid) { diff --git a/user-service/src/main/java/net/explorviz/security/server/resources/HttpStatus.java b/user-service/src/main/java/net/explorviz/security/server/resources/HttpStatus.java new file mode 100644 index 00000000..75c33306 --- /dev/null +++ b/user-service/src/main/java/net/explorviz/security/server/resources/HttpStatus.java @@ -0,0 +1,13 @@ +package net.explorviz.security.server.resources; + +/** + * Contains HTTP status codes as string constants. + * Mainly to be used for Swagger annotations. + */ +public class HttpStatus { + + + public static final String OK = "200"; + public static final String BAD_REQUEST = "400"; + +} diff --git a/user-service/src/main/java/net/explorviz/security/server/resources/RoleResource.java b/user-service/src/main/java/net/explorviz/security/server/resources/RoleResource.java index f4a79cf2..b486dc9a 100644 --- a/user-service/src/main/java/net/explorviz/security/server/resources/RoleResource.java +++ b/user-service/src/main/java/net/explorviz/security/server/resources/RoleResource.java @@ -12,8 +12,8 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; -import net.explorviz.shared.security.filters.Secure; import net.explorviz.security.user.Role; +import net.explorviz.shared.security.filters.Secure; /** * Provides endpoints for user roles. diff --git a/user-service/src/main/java/net/explorviz/security/server/resources/TokenResource.java b/user-service/src/main/java/net/explorviz/security/server/resources/TokenResource.java index 73cf4846..f27df35a 100644 --- a/user-service/src/main/java/net/explorviz/security/server/resources/TokenResource.java +++ b/user-service/src/main/java/net/explorviz/security/server/resources/TokenResource.java @@ -19,11 +19,11 @@ import net.explorviz.security.model.Token; import net.explorviz.security.model.UserCredentials; import net.explorviz.security.services.TokenService; -import net.explorviz.security.services.exceptions.UserValidationService; +import net.explorviz.security.services.UserValidationService; import net.explorviz.security.user.User; import net.explorviz.shared.security.TokenBasedSecurityContext; -import net.explorviz.shared.security.filters.Secure; import net.explorviz.shared.security.TokenDetails; +import net.explorviz.shared.security.filters.Secure; /** @@ -58,14 +58,14 @@ public class TokenResource { @Produces(MEDIA_TYPE) @Operation(description = "Request an API token") @ApiResponse(responseCode = "200", - description = "If the credentials are valid, the associated user is returned. " - + "The object includes a fresh bearer token to be used for authentication" - + "and authorization at all services." - + "The token expires after 1 hour and can be refreshed once ", - content = @Content(schema = @Schema(implementation = User.class))) + description = "If the credentials are valid, the associated user is returned. " + + "The object includes a fresh bearer token to be used for authentication" + + "and authorization at all services." + + "The token expires after 1 hour and can be refreshed once ", + content = @Content(schema = @Schema(implementation = User.class))) @ApiResponse(responseCode = "403", description = "Invalid credentials.") @RequestBody(description = "The credentials", - content = @Content(schema = @Schema(implementation = UserCredentials.class))) + content = @Content(schema = @Schema(implementation = UserCredentials.class))) public User issueToken(final UserCredentials credentials) { // curl -X POST @@ -94,9 +94,9 @@ public User issueToken(final UserCredentials credentials) { + "The HTTP POST body must not contain data and the " + "to-be refreshed token inside of the ' Authorization: Bearer' header.") @ApiResponse(responseCode = "200", - description = "New token, which again is valid for 1 hour. " - + "A refreshed token can't be refreshed further.", - content = @Content(schema = @Schema(implementation = User.class))) + description = "New token, which again is valid for 1 hour. " + + "A refreshed token can't be refreshed further.", + content = @Content(schema = @Schema(implementation = User.class))) @ApiResponse(responseCode = "403", description = "Token can't be refreshed.") @SecurityRequirement(name = "token") @Secure diff --git a/user-service/src/main/java/net/explorviz/security/server/resources/UserResource.java b/user-service/src/main/java/net/explorviz/security/server/resources/UserResource.java index b6491c3d..2ea51a74 100644 --- a/user-service/src/main/java/net/explorviz/security/server/resources/UserResource.java +++ b/user-service/src/main/java/net/explorviz/security/server/resources/UserResource.java @@ -1,7 +1,7 @@ package net.explorviz.security.server.resources; -import com.mongodb.DuplicateKeyException; -import com.mongodb.MongoException; +import static org.eclipse.jetty.http.HttpStatus.NO_CONTENT_204; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -35,25 +35,23 @@ import net.explorviz.security.services.UserService; import net.explorviz.security.services.exceptions.DuplicateUserException; import net.explorviz.security.services.exceptions.UserCrudException; +import net.explorviz.security.user.Role; import net.explorviz.security.user.User; import net.explorviz.security.util.PasswordStorage; import net.explorviz.security.util.PasswordStorage.CannotPerformOperationException; import net.explorviz.shared.querying.Query; import net.explorviz.shared.querying.QueryResult; -import net.explorviz.security.user.Role; import net.explorviz.shared.security.filters.Secure; -import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides several Endpoints for user management. - * */ @Path("v1/users") @Tag(name = "User") @SecurityScheme(type = SecuritySchemeType.HTTP, name = "token", scheme = "bearer", - bearerFormat = "JWT") + bearerFormat = "JWT") @SecurityRequirement(name = "token") @Secure public class UserResource { @@ -64,26 +62,20 @@ public class UserResource { private static final String MSG_INVALID_PASSWORD = "Invalid password"; private static final String MSG_INVALID_USERNAME = "Invalid username"; - private static final String MSG_USER_NOT_RETRIEVED = "Could not retrieve user "; private static final String MSG_UNKOWN_ROLE = "Unknown role"; private final UserService userCrudService; - private final BatchRequestResource batchSubResource; - /** * Constructor for this class. * * @param userCrudService - Service to obtain actual users. - * @param batchSubResource - Sub Resource Class. */ @Inject - public UserResource(final UserService userCrudService, - final BatchRequestResource batchSubResource) { + public UserResource(final UserService userCrudService) { this.userCrudService = userCrudService; - this.batchSubResource = batchSubResource; } // CHECKSTYLE.OFF: Cyclomatic @@ -99,10 +91,12 @@ public UserResource(final UserService userCrudService, @Produces(MEDIA_TYPE) @RolesAllowed({Role.ADMIN_NAME}) @Operation(summary = "Create a new user") - @ApiResponse(responseCode = "200", description = "User created", - content = @Content(mediaType = MEDIA_TYPE, schema = @Schema(implementation = User.class))) - @ApiResponse(responseCode = "400", - description = "Invalid properties for the user or user with the given name already exists.") + @ApiResponse(responseCode = HttpStatus.OK, description = "User created", + content = @Content(mediaType = MEDIA_TYPE, + schema = @Schema(implementation = User.class))) + @ApiResponse(responseCode = HttpStatus.BAD_REQUEST, + description = "Invalid properties for the user or user with the given " + + "name already exists.") @RequestBody( description = "The user to be created. " + "Both the password and the name must not be empty. " @@ -115,6 +109,7 @@ public User newUser(final User user) { // NOPMD throw new BadRequestException(MSG_INVALID_USERNAME); } + if (user.getPassword() == null || user.getPassword().equals("")) { throw new BadRequestException(MSG_INVALID_PASSWORD); } @@ -146,7 +141,7 @@ public User newUser(final User user) { // NOPMD throw new BadRequestException("User already exists", ex); } catch (final UserCrudException ex) { LOGGER.error("Error saving user", ex); - throw new InternalServerErrorException(); + throw new InternalServerErrorException(ex); } } @@ -154,9 +149,9 @@ public User newUser(final User user) { // NOPMD * Updates the details of a already existing user. The values of the targeted user will be * overridden by the values of an updatedUser. All attributes that are {@code null} are ignored. * - * @param id - the id of the user to update + * @param id - the id of the user to update * @param updatedUser - a {@link User} object containing the changes. All fields set to - * {@code null} will be ignored when updating. + * {@code null} will be ignored when updating. * @return the updated user */ @PATCH @@ -165,71 +160,19 @@ public User newUser(final User user) { // NOPMD @Produces(MEDIA_TYPE) @Consumes(MEDIA_TYPE) @Operation(summary = "Update an existing User") - @ApiResponse(responseCode = "200", description = "Updated user", - content = @Content(mediaType = MEDIA_TYPE, schema = @Schema(implementation = User.class))) + @ApiResponse(responseCode = HttpStatus.OK, description = "Updated user", + content = @Content(mediaType = MEDIA_TYPE, + schema = @Schema(implementation = User.class))) @ApiResponse(responseCode = "400", description = "Properties to update are invalid") public User updateUser( @Parameter(description = "Id of the user to update") @PathParam("id") final String id, @Parameter(description = "Updated values") final User updatedUser) { // NOPMD - User targetUser = null; - try { - targetUser = - this.userCrudService.getEntityById(id).orElseThrow(() -> new NotFoundException()); - } catch (final MongoException ex) { - if (LOGGER.isErrorEnabled()) { - LOGGER.error(MSG_USER_NOT_RETRIEVED + ex.getMessage() + " (" + ex.getCode() + ")"); - } - throw new InternalServerErrorException(ex); - } - - - if (updatedUser.getId() != null && !updatedUser.getId().equals(id)) { // NOPMD - LOGGER.info("Won't update id"); - throw new BadRequestException("Can't update id, leave null"); - } - - if (updatedUser.getPassword() != null) { - if (updatedUser.getPassword().equals("")) { - throw new BadRequestException(MSG_INVALID_PASSWORD); - } - try { - targetUser.setPassword(PasswordStorage.createHash(updatedUser.getPassword())); - } catch (final CannotPerformOperationException e) { - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Could not update user due to password hashing failure: " + e.getMessage()); - } - throw new InternalServerErrorException(e); - } - } - - if (updatedUser.getUsername() != null) { - if (updatedUser.getUsername().equals("")) { - throw new BadRequestException(MSG_INVALID_USERNAME); - } - - targetUser.setUsername(updatedUser.getUsername()); - } - - if (updatedUser.getRoles() != null) { - for (final String r : updatedUser.getRoles()) { - if (!Role.exists(r)) { - throw new BadRequestException("Unknown role: " + r); - } - } - targetUser.setRoles(updatedUser.getRoles()); - } - - try { - this.userCrudService.updateEntity(targetUser); - } catch (final DuplicateKeyException ex) { - throw new BadRequestException("Username already exists", ex); - + return this.userCrudService.updateEntity(id, updatedUser); + } catch (UserCrudException e) { + throw new BadRequestException(e.getMessage(), e); } - - return targetUser; - } /** @@ -242,17 +185,20 @@ public User updateUser( @Produces(MEDIA_TYPE) @Operation(description = "List all users") @Parameters({ - @Parameter(in = ParameterIn.QUERY, name = "page[size]", - description = "Controls the size, i.e., amount of entities, of each page."), - @Parameter(in = ParameterIn.QUERY, name = "page[number]", - description = "Index of the page to return."), - @Parameter(in = ParameterIn.QUERY, name = "filter[roles]", - description = "Restricts the result to the given role(s)."), - @Parameter(in = ParameterIn.QUERY, name = "filter[batchid]", - description = "Only return users that were created by the batch request " - + "with the specified id.")}) - @ApiResponse(responseCode = "200", description = "List of users matching the request", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = User.class)))) + @Parameter(in = ParameterIn.QUERY, name = "page[size]", + description = "Controls the size, i.e., amount of entities, " + + "of each page."), + @Parameter(in = ParameterIn.QUERY, name = "page[number]", + description = "Index of the page to return."), + @Parameter(in = ParameterIn.QUERY, name = "filter[roles]", + description = "Restricts the result to the given role(s)."), + @Parameter(in = ParameterIn.QUERY, name = "filter[batchid]", + description = + "Only return users that were created by the batch request " + + "with the specified id.")}) + @ApiResponse(responseCode = HttpStatus.OK, description = "List of users matching the request", + content = @Content( + array = @ArraySchema(schema = @Schema(implementation = User.class)))) public QueryResult find(@Context final UriInfo uri) { final Query query = Query.fromParameterMap(uri.getQueryParameters(true)); return this.userCrudService.query(query); @@ -269,22 +215,19 @@ public QueryResult find(@Context final UriInfo uri) { @RolesAllowed({Role.ADMIN_NAME, Role.USER_NAME}) @Produces(MEDIA_TYPE) @Operation(summary = "Find a user by its id") - @ApiResponse(responseCode = "200", description = "The requested user", - content = @Content(mediaType = MEDIA_TYPE, schema = @Schema(implementation = User.class))) + @ApiResponse(responseCode = HttpStatus.OK, description = "The requested user", + content = @Content(mediaType = MEDIA_TYPE, + schema = @Schema(implementation = User.class))) @ApiResponse(responseCode = "404", description = "No such user") public User userById( @Parameter(description = "Unique id of the user to find") @PathParam("id") final String id) { - if (id == null || "".equals(id)) { + if (id == null || id.isEmpty()) { throw new BadRequestException("Invalid id"); } // TODO if a user is a normal user check if he tries to get just his own data? - User foundUser = null; - foundUser = this.userCrudService.getEntityById(id).orElseThrow(() -> new NotFoundException()); - - this.userCrudService.updateEntity(foundUser); - return foundUser; + return this.userCrudService.getEntityById(id).orElseThrow(NotFoundException::new); } /** @@ -297,9 +240,9 @@ public User userById( @Path("{id}") @RolesAllowed({Role.ADMIN_NAME}) @Operation(summary = "Remove a user identified by its Id") - @ApiResponse(responseCode = "400", - description = "Attempt to delete the last existing use with the admin role, " - + "which is not possible") + @ApiResponse(responseCode = HttpStatus.BAD_REQUEST, + description = "Attempt to delete the last existing use with the admin role, " + + "which is not possible") @ApiResponse(responseCode = "204", description = "User deleted") public Response removeUser(@Parameter( description = "Unique id of the user to delete") @PathParam("id") final String id) { @@ -314,7 +257,7 @@ public Response removeUser(@Parameter( } - return Response.status(HttpStatus.NO_CONTENT_204).build(); + return Response.status(NO_CONTENT_204).build(); } /** @@ -327,13 +270,13 @@ public Response removeUser(@Parameter( @RolesAllowed({Role.ADMIN_NAME}) @Operation(summary = "Delete a list of users") @ApiResponse(responseCode = "204", description = "All user deleted") - @ApiResponse(responseCode = "400", - description = "Attempt to delete the last existing use with the admin role, " - + "which is not possible") + @ApiResponse(responseCode = HttpStatus.BAD_REQUEST, + description = "Attempt to delete the last existing use with the admin role, " + + "which is not possible") public Response removeAll(final List users) { users.forEach(u -> this.removeUser(u.getId())); - return Response.status(HttpStatus.NO_CONTENT_204).build(); + return Response.status(NO_CONTENT_204).build(); } diff --git a/user-service/src/main/java/net/explorviz/security/services/BatchService.java b/user-service/src/main/java/net/explorviz/security/services/BatchService.java index ef7cbf68..f48677ff 100644 --- a/user-service/src/main/java/net/explorviz/security/services/BatchService.java +++ b/user-service/src/main/java/net/explorviz/security/services/BatchService.java @@ -38,16 +38,19 @@ /** * Helper service for batch creation of users. - * */ @Service public class BatchService { + public static final int MAX_COUNT = 300; + private static final Logger LOGGER = LoggerFactory.getLogger(BatchService.class); private static final String MEDIA_TYPE = "application/vnd.api+json"; private static final String HTTP = "http://"; private static final String MSG_FAIL = "Batch request failed, rolling back"; + + private final UserService userService; @@ -61,14 +64,15 @@ public class BatchService { /** * Creates a new service. * - * @param userService instance of {@link UserService} - * @param converter instance of {@link ResourceConverter} + * @param userService instance of {@link UserService} + * @param converter instance of {@link ResourceConverter} * @param settingServiceHost host of the settings service */ @Inject public BatchService(final UserService userService, final ResourceConverter converter, - final IdGenerator idGen, @Config("services.settings") final String settingServiceHost, - @Config("services.settings.preferences") final String settingsPrefPath) { + final IdGenerator idGen, + @Config("services.settings") final String settingServiceHost, + @Config("services.settings.preferences") final String settingsPrefPath) { this.userService = userService; this.settingsServiceHost = settingServiceHost; this.settingsPrefPath = settingsPrefPath; @@ -79,16 +83,16 @@ public BatchService(final UserService userService, final ResourceConverter conve /** * Creates and persists a set of users. * - * @param batch the batch request + * @param batch the batch request * @param authHeader of an admin user * @return as list of all users created * @throws UserCrudException if the batch creation was unsuccessful. If this exception is thrown, - * no user is persisted. + * no user is persisted. */ public List create(final UserBatchRequest batch, final String authHeader) throws UserCrudException { - + validate(batch); final List createdUsers = new ArrayList<>(); final List createdPrefs = new ArrayList<>(); final String batchId = this.idGenerator.generateId(); @@ -105,7 +109,7 @@ public List create(final UserBatchRequest batch, final String authHeader) LOGGER.error(MSG_FAIL); } this.rollbackUsers(createdUsers); - throw new UserCrudException("Could not hash password"); + throw new UserCrudException("Could not hash password", e1); } @@ -128,6 +132,22 @@ public List create(final UserBatchRequest batch, final String authHeader) } + private void validate(final UserBatchRequest batch) throws MalformedBatchRequestException { + if (batch.getCount() > MAX_COUNT) { + throw new MalformedBatchRequestException("Count must be smaller than " + MAX_COUNT); + } + + if (batch.getCount() == 0) { + throw new MalformedBatchRequestException("Count must be bigger than 0"); + } + if (batch.getPrefix() == null || batch.getPrefix().isEmpty()) { + throw new MalformedBatchRequestException("Prefix can't be empty"); + } + if (batch.getPasswords() == null || batch.getPasswords().size() != batch.getCount()) { + throw new MalformedBatchRequestException("Passwords must match size of users to create"); + } + } + /** * Rolls back previously created users. * @@ -150,7 +170,7 @@ private void rollbackUsers(final List created) { * Rolls back all preferences given by dispatching DELETE requests. * * @param created preferences to delete - * @param auth http auth header header to authorize at the settings service + * @param auth http auth header header to authorize at the settings service */ private void rollbackPrefs(final List created, final String auth) { @@ -173,7 +193,7 @@ private void rollbackPrefs(final List created, final String auth) { final Response r = i.invoke(); - if (r.getStatus() != HttpStatus.NO_CONTENT_204) { + if (r.getStatus() != HttpStatus.NO_CONTENT_204 && LOGGER.isErrorEnabled()) { LOGGER.error("Deletion of a preference failed, id: " + id); } } @@ -184,15 +204,13 @@ private void rollbackPrefs(final List created, final String auth) { * Creates the preferences defined in {@code prefs} for the given user, by dispatching an HTTP * request to the Settings-Service. * - * @param user the user to create the preferences for + * @param user the user to create the preferences for * @param prefs the settings with the values - * * @return a list of the ids of the created settings - * * @throws UserCrudException if the creation of a single preference failed */ private List createPrefs(final User user, final Map prefs, - final String auth) throws UserCrudException { + final String auth) throws UserCrudException { final List ids = new ArrayList<>(); @@ -209,7 +227,6 @@ private List createPrefs(final User user, final Map pref final Invocation.Builder invocationBuilder = target.request(MEDIA_TYPE).header(HttpHeaders.AUTHORIZATION, auth); - System.out.println(HTTP + this.settingsServiceHost + this.settingsPrefPath); // Create a request for each entry for (final Entry pref : prefs.entrySet()) { @@ -235,7 +252,9 @@ private List createPrefs(final User user, final Map pref || r.getStatus() == HttpStatus.NOT_FOUND_404) { throw new MalformedBatchRequestException(err.getDetail()); } else { - LOGGER.error("Unkown settings-service error: " + err.getDetail()); + if (LOGGER.isErrorEnabled()) { + LOGGER.error("Unkown settings-service error: " + err.getDetail()); + } throw new UserCrudException(err.getDetail()); } } @@ -249,7 +268,7 @@ private List createPrefs(final User user, final Map pref throw new UserCrudException("Settings-Service unreachable", e); } else { LOGGER.error("Could not create preferences: ", e); - throw new UserCrudException("Could not create preferences"); + throw new UserCrudException("Could not create preferences", e); } } } @@ -259,7 +278,8 @@ private List createPrefs(final User user, final Map pref } private User newUser(final String pref, final int num, final String password, - final List roles, final String batchId) throws CannotPerformOperationException { + final List roles, final String batchId) + throws CannotPerformOperationException { final StringBuilder sb = new StringBuilder(); final String name = sb.append(pref).append('-').append(num).toString(); @@ -273,20 +293,24 @@ private User newUser(final String pref, final int num, final String password, /** * Delets a users that belong to the given batch id. - * + * * @param batchId The id of the batch to delete all users of */ - public void deleteBatch(String batchId) { - MultivaluedHashMap queryParams = new MultivaluedHashMap<>(); + public void deleteBatch(final String batchId) { + final MultivaluedHashMap queryParams = new MultivaluedHashMap<>(); queryParams.putSingle("filter[batchid]", batchId); - Query batchQuery = Query.fromParameterMap(queryParams); - QueryResult res = userService.query(batchQuery); - LOGGER.info("Delete batch of " + res.getData().size() + " users"); + final Query batchQuery = Query.fromParameterMap(queryParams); + final QueryResult res = userService.query(batchQuery); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Delete batch of " + res.getData().size() + " users"); + } res.getData().forEach(u -> { try { this.userService.deleteEntityById(u.getId()); } catch (final UserCrudException e) { - LOGGER.warn("Skipped a user during batch deletion: " + e.getMessage()); + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Skipped a user during batch deletion: " + e.getMessage()); + } } }); } diff --git a/user-service/src/main/java/net/explorviz/security/services/KafkaUserService.java b/user-service/src/main/java/net/explorviz/security/services/KafkaUserService.java index 9b390090..06870bbe 100644 --- a/user-service/src/main/java/net/explorviz/security/services/KafkaUserService.java +++ b/user-service/src/main/java/net/explorviz/security/services/KafkaUserService.java @@ -62,8 +62,14 @@ public void publishCreated(final String createdId) throws JsonProcessingExceptio LOGGER.info("Published creation event to kafka"); } - static class UserEvent { + /** + * Written to a Kafka topic to notify other services about user events. + */ + public static class UserEvent { + /** + * Type of the event. Deletion or creation. + */ public enum EventType { CREATED, DELETED; } diff --git a/user-service/src/main/java/net/explorviz/security/services/UserService.java b/user-service/src/main/java/net/explorviz/security/services/UserService.java index eea9f64b..9a759104 100644 --- a/user-service/src/main/java/net/explorviz/security/services/UserService.java +++ b/user-service/src/main/java/net/explorviz/security/services/UserService.java @@ -9,9 +9,12 @@ import java.util.NoSuchElementException; import java.util.Optional; import javax.inject.Inject; +import javax.ws.rs.BadRequestException; import net.explorviz.security.services.exceptions.DuplicateUserException; import net.explorviz.security.services.exceptions.UserCrudException; +import net.explorviz.security.user.Role; import net.explorviz.security.user.User; +import net.explorviz.security.util.PasswordStorage; import net.explorviz.shared.common.idgen.IdGenerator; import net.explorviz.shared.querying.Query; import net.explorviz.shared.querying.QueryResult; @@ -31,7 +34,6 @@ *

  • password: hashed password
  • *
  • roles: list of role that are assigned to the user
  • * - * */ @Service public class UserService implements Queryable { @@ -40,7 +42,8 @@ public class UserService implements Queryable { private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); - + private static final String MSG_INVALID_PASSWORD = "Invalid password"; + private static final String MSG_INVALID_USERNAME = "Invalid username"; private final Datastore datastore; @@ -56,7 +59,7 @@ public class UserService implements Queryable { */ @Inject public UserService(final Datastore datastore, final IdGenerator idGenerator, - final KafkaUserService kafkaService) { + final KafkaUserService kafkaService) { this.idGenerator = idGenerator; this.datastore = datastore; this.kafkaService = kafkaService; @@ -87,7 +90,7 @@ public User saveNewEntity(final User user) throws UserCrudException { } } catch (final DuplicateKeyException e) { throw new DuplicateUserException( - String.format("User with %s already exists", user.getUsername())); + String.format("User with %s already exists", user.getUsername()), e); } catch (final MongoException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Could not save user: " + e.getMessage()); @@ -103,8 +106,62 @@ public User saveNewEntity(final User user) throws UserCrudException { return user; } - public void updateEntity(final User user) { - this.datastore.save(user); + /** + * Updates a user. + * + * @param id the id if the user to update + * @param update the updated user entity + * @throws UserCrudException if no user with the given ID is found or the update is invalid + */ + public User updateEntity(final String id, final User update) throws UserCrudException { + + if (update.getId() != null && !update.getId().equals(id)) { // NOPMD + LOGGER.info("Won't update id"); + throw new UserCrudException("Can't update id, leave null"); + } + + final User targetUser = + getEntityById(id).orElseThrow(() -> new UserCrudException("No user with such id")); + + + + if (update.getPassword() != null) { + if (update.getPassword().equals("")) { + throw new BadRequestException(MSG_INVALID_PASSWORD); + } + try { + targetUser.setPassword(PasswordStorage.createHash(update.getPassword())); + } catch (final PasswordStorage.CannotPerformOperationException e) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Could not update user due to password hashing failure: " + e.getMessage()); + } + throw new UserCrudException(MSG_INVALID_PASSWORD, e); + } + } + + if (update.getUsername() != null) { + if (update.getUsername().equals("")) { + throw new UserCrudException(MSG_INVALID_USERNAME); + } + + targetUser.setUsername(update.getUsername()); + } + + if (update.getRoles() != null) { + for (final String r : update.getRoles()) { + if (!Role.exists(r)) { + throw new UserCrudException("Unknown role: " + r); + } + } + targetUser.setRoles(update.getRoles()); + } + + try { + this.datastore.save(targetUser); + return targetUser; + } catch (final DuplicateKeyException ex) { + throw new UserCrudException("Username already exists", ex); + } } /** @@ -112,7 +169,7 @@ public void updateEntity(final User user) { * * @param id the id of the user to retrieve * @return and {@link Optional} which contains the user with given id or is empty if such a user - * does not exist + * does not exist */ public Optional getEntityById(final String id) { @@ -126,7 +183,7 @@ public Optional getEntityById(final String id) { * * @param id id of the user to delete * @throws UserCrudException if the id belongs to the a user with the admin role and there are no - * other admin users. This prevents the deletion of the last admin. + * other admin users. This prevents the deletion of the last admin. */ public void deleteEntityById(final String id) throws UserCrudException { @@ -155,7 +212,7 @@ public void deleteEntityById(final String id) throws UserCrudException { * * @param id user id * @return {@code true} iff the user with the given id has the role "admin" and there is no other - * user with this role. + * user with this role. */ private boolean isLastAdmin(final String id) { User user; diff --git a/user-service/src/main/java/net/explorviz/security/services/exceptions/UserValidationService.java b/user-service/src/main/java/net/explorviz/security/services/UserValidationService.java similarity index 94% rename from user-service/src/main/java/net/explorviz/security/services/exceptions/UserValidationService.java rename to user-service/src/main/java/net/explorviz/security/services/UserValidationService.java index e0d46a1f..29e6e8c4 100644 --- a/user-service/src/main/java/net/explorviz/security/services/exceptions/UserValidationService.java +++ b/user-service/src/main/java/net/explorviz/security/services/UserValidationService.java @@ -1,14 +1,13 @@ -package net.explorviz.security.services.exceptions; +package net.explorviz.security.services; import javax.inject.Inject; import javax.ws.rs.ForbiddenException; import net.explorviz.security.model.UserCredentials; -import net.explorviz.security.services.UserService; +import net.explorviz.security.user.User; import net.explorviz.security.util.PasswordStorage; import net.explorviz.security.util.PasswordStorage.CannotPerformOperationException; import net.explorviz.security.util.PasswordStorage.InvalidHashException; import org.jvnet.hk2.annotations.Service; -import net.explorviz.security.user.User; /** * Injectable service that contains utility methods for {@link UserCredentials} validation. diff --git a/user-service/src/main/java/net/explorviz/security/util/PasswordStorage.java b/user-service/src/main/java/net/explorviz/security/util/PasswordStorage.java index ef182c8d..d0073513 100644 --- a/user-service/src/main/java/net/explorviz/security/util/PasswordStorage.java +++ b/user-service/src/main/java/net/explorviz/security/util/PasswordStorage.java @@ -15,6 +15,7 @@ * "https://github.com/defuse/password-hashing">https://github.com/defuse/password-hashing * */ +@SuppressWarnings("PMD") public class PasswordStorage { // CHECKSTYLE.OFF: javadoc diff --git a/user-service/src/test/java/net/explorviz/security/server/resources/RoleResourceTest.java b/user-service/src/test/java/net/explorviz/security/server/resources/RoleResourceTest.java index 8ca3c6c6..c69b6279 100644 --- a/user-service/src/test/java/net/explorviz/security/server/resources/RoleResourceTest.java +++ b/user-service/src/test/java/net/explorviz/security/server/resources/RoleResourceTest.java @@ -1,7 +1,7 @@ package net.explorviz.security.server.resources; - import static org.junit.jupiter.api.Assertions.assertTrue; + import net.explorviz.security.user.Role; import org.junit.jupiter.api.Test; diff --git a/user-service/src/test/java/net/explorviz/security/server/resources/UserResourceTest.java b/user-service/src/test/java/net/explorviz/security/server/resources/UserResourceTest.java index 9481e381..7aab552b 100644 --- a/user-service/src/test/java/net/explorviz/security/server/resources/UserResourceTest.java +++ b/user-service/src/test/java/net/explorviz/security/server/resources/UserResourceTest.java @@ -1,11 +1,11 @@ package net.explorviz.security.server.resources; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -16,17 +16,15 @@ import java.util.Optional; import java.util.stream.Collectors; import javax.ws.rs.BadRequestException; +import javax.ws.rs.NotFoundException; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.UriInfo; import net.explorviz.security.services.UserService; import net.explorviz.security.services.exceptions.UserCrudException; +import net.explorviz.security.user.Role; import net.explorviz.security.user.User; -import net.explorviz.security.util.PasswordStorage; -import net.explorviz.security.util.PasswordStorage.CannotPerformOperationException; -import net.explorviz.security.util.PasswordStorage.InvalidHashException; import net.explorviz.shared.querying.Query; import net.explorviz.shared.querying.QueryResult; -import net.explorviz.security.user.Role; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,12 +34,14 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck + + /** * Unit tests for {@link UserResource}. All tests are performed by just calling the methods of * {@link UserResource}. */ @ExtendWith(MockitoExtension.class) -@SuppressWarnings("PMD") class UserResourceTest { @InjectMocks @@ -69,9 +69,8 @@ public void setUp() throws UserCrudException { return newUser; }); - Mockito.lenient().when(this.userCrudService.getEntityById(any())).thenAnswer(inv -> { - return Optional.ofNullable(this.users.get(inv.getArgument(0))); - }); + Mockito.lenient().when(this.userCrudService.getEntityById(any())) + .thenAnswer(inv -> Optional.ofNullable(this.users.get(inv.getArgument(0)))); Mockito.lenient().doAnswer(inv -> { this.users.remove(inv.getArgument(0)); @@ -79,7 +78,7 @@ public void setUp() throws UserCrudException { }).when(this.userCrudService).deleteEntityById(any()); Mockito.lenient().when(this.userCrudService.query(any())).thenAnswer(inv -> { - final Query query = (Query) inv.getArgument(0); + final Query query = inv.getArgument(0); Collection data = this.users.values(); if (query.doFilter()) { final String role = query.getFilters().get("role").get(0); @@ -92,9 +91,10 @@ public void setUp() throws UserCrudException { }); - Mockito.lenient().when(this.userCrudService.getAll()).thenAnswer(inv -> { - return this.users.values().stream().collect(Collectors.toList()); - }); + Mockito.lenient().when(this.userCrudService.getAll()) + .thenAnswer(inv -> new ArrayList<>(this.users.values())); + + } @@ -198,61 +198,9 @@ public void testRemoveUser() { final User newUser = this.userResource.newUser(u1); this.userResource.removeUser(newUser.getId()); - } - - @Test - public void testChangePassword() throws CannotPerformOperationException, InvalidHashException { - - // Will always fail if passwords are hashed - - final User u1 = new User("testuser"); - u1.setPassword("password"); - final User newUser = this.userResource.newUser(u1); - final String uid = newUser.getId(); - - final User update = new User(null, null, "newpw", null); - - final User updatedUser = this.userResource.updateUser(uid, update); - - assertTrue(PasswordStorage.verifyPassword("newpw", updatedUser.getPassword())); - assertEquals(newUser.getId(), updatedUser.getId()); - assertEquals(u1.getUsername(), updatedUser.getUsername()); - assertEquals(u1.getRoles(), updatedUser.getRoles()); - } - - - @Test - public void testChangeUsername() { - final User u1 = new User("testuser"); - u1.setPassword("password"); - final User newUser = this.userResource.newUser(u1); - final String uid = newUser.getId(); - - final User update = new User(null, "newname", null, null); - final User updatedUser = this.userResource.updateUser(uid, update); - - assertEquals("newname", updatedUser.getUsername()); - assertEquals(newUser.getId(), updatedUser.getId()); - assertEquals(u1.getRoles(), updatedUser.getRoles()); - assertEquals(u1.getPassword(), updatedUser.getPassword()); - } - - - @Test - public void testChangeRoles() { - final User u1 = new User("testuser"); - u1.setPassword("password"); - final User newUser = this.userResource.newUser(u1); - - final String uid = newUser.getId(); - - final User update = new User(null, null, null, Arrays.asList(Role.USER_NAME)); - final User updatedUser = this.userResource.updateUser(uid, update); - assertTrue(updatedUser.getRoles().stream().anyMatch(r -> r.equals(Role.USER_NAME))); - assertEquals(newUser.getId(), updatedUser.getId()); - assertEquals(u1.getUsername(), updatedUser.getUsername()); - assertEquals(u1.getPassword(), updatedUser.getPassword()); + assertThrows(NotFoundException.class, + () -> this.userResource.userById(newUser.getId())); } diff --git a/user-service/src/test/java/net/explorviz/security/services/BatchCreationServiceTest.java b/user-service/src/test/java/net/explorviz/security/services/BatchCreationServiceTest.java index db3a1acf..316075d2 100644 --- a/user-service/src/test/java/net/explorviz/security/services/BatchCreationServiceTest.java +++ b/user-service/src/test/java/net/explorviz/security/services/BatchCreationServiceTest.java @@ -1,15 +1,16 @@ package net.explorviz.security.services; import static org.junit.jupiter.api.Assertions.assertEquals; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import net.explorviz.security.model.UserBatchRequest; import net.explorviz.security.services.exceptions.UserCrudException; +import net.explorviz.security.user.Role; import net.explorviz.security.user.User; import net.explorviz.shared.common.idgen.IdGenerator; -import net.explorviz.security.user.Role; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,7 +23,6 @@ /** * Unit test for {@link BatchService}. - * */ @ExtendWith(MockitoExtension.class) public class BatchCreationServiceTest { @@ -35,8 +35,6 @@ public class BatchCreationServiceTest { private final AtomicInteger id = new AtomicInteger(0); - private UserService userService; - private BatchService bcs; private final List users = new ArrayList<>(); diff --git a/user-service/src/test/java/net/explorviz/security/services/UserServiceTest.java b/user-service/src/test/java/net/explorviz/security/services/UserServiceTest.java index cca6ca3d..98b08ac8 100644 --- a/user-service/src/test/java/net/explorviz/security/services/UserServiceTest.java +++ b/user-service/src/test/java/net/explorviz/security/services/UserServiceTest.java @@ -1,11 +1,20 @@ package net.explorviz.security.services; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.ws.rs.core.MultivaluedHashMap; +import net.explorviz.security.services.exceptions.UserCrudException; +import net.explorviz.security.user.Role; import net.explorviz.security.user.User; +import net.explorviz.security.util.PasswordStorage; +import net.explorviz.shared.common.idgen.IdGenerator; import net.explorviz.shared.querying.Query; import net.explorviz.shared.querying.QueryResult; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -13,16 +22,17 @@ import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; import xyz.morphia.Datastore; import xyz.morphia.query.FindOptions; +// CHECKSTYLE.OFF: MultipleStringLiteralsCheck +// CHECKSTYLE.OFF: MagicNumberCheck + + /** * Unit test for {@link UserService}. - * - * */ @ExtendWith(MockitoExtension.class) public class UserServiceTest { @@ -34,18 +44,23 @@ public class UserServiceTest { @Mock private Datastore store; + @Mock + private IdGenerator idGen; + + @Mock + private KafkaUserService kus; + private void fillUsers(final int size) { this.users = new ArrayList<>(); - final String prefix = "user-"; for (int i = 0; i < size; i++) { - final User u = new User("" + i, prefix + i, "abc", null); + final User u = new User(String.valueOf(i), "user-" + i, "abc", null); this.users.add(u); } } @BeforeEach public void setUp() { - this.userService = new UserService(this.store, null, null); + this.userService = new UserService(this.store, idGen, kus); } @Test @@ -56,23 +71,17 @@ public void testPaginationFull() { this.fillUsers(usersTotal); final MultivaluedHashMap paginationParams = new MultivaluedHashMap<>(); - paginationParams.add("page[number]", "" + pageIndex); - paginationParams.add("page[size]", "" + pageLen); + paginationParams.add("page[number]", String.valueOf(pageIndex)); + paginationParams.add("page[size]", String.valueOf(pageLen)); final Query paginationQuery = Query.fromParameterMap(paginationParams); final xyz.morphia.query.Query q = Mockito.mock(xyz.morphia.query.Query.class); Mockito.when(this.store.createQuery(User.class)).thenReturn(q); - Mockito.doAnswer(new Answer>() { - - @Override - public List answer(final InvocationOnMock invocation) throws Throwable { - final FindOptions options = invocation.getArgument(0, FindOptions.class); - final int skip = options.getSkip(); - final int limit = options.getLimit(); - final List r = - new ArrayList<>(UserServiceTest.this.users.subList(skip, skip + limit)); - return r; - } + Mockito.doAnswer((Answer>) invocation -> { + final FindOptions options = invocation.getArgument(0, FindOptions.class); + final int skip = options.getSkip(); + final int limit = options.getLimit(); + return new ArrayList<>(users.subList(skip, skip + limit)); }).when(q).asList(ArgumentMatchers.any(FindOptions.class)); final QueryResult returned = this.userService.query(paginationQuery); @@ -80,4 +89,76 @@ public List answer(final InvocationOnMock invocation) throws Throwable { Assert.assertEquals(pageLen, returned.getN()); } + @Test + public void testChangePassword() throws PasswordStorage.CannotPerformOperationException, + PasswordStorage.InvalidHashException, UserCrudException { + + final String id = RandomStringUtils.random(5); + Mockito.when(idGen.generateId()).thenReturn(id); + final User u1 = new User("testuser"); + u1.setPassword("password"); + + + final User newUser = this.userService.saveNewEntity(u1); + Mockito.when(store.get(User.class, id)).thenReturn(newUser); + + final String uid = newUser.getId(); + + final User update = new User(null, null, "newpw", null); + + final User updatedUser = this.userService.updateEntity(uid, update); + + assertTrue(PasswordStorage.verifyPassword("newpw", updatedUser.getPassword())); + assertEquals(newUser.getId(), updatedUser.getId()); + assertEquals(u1.getUsername(), updatedUser.getUsername()); + assertEquals(u1.getRoles(), updatedUser.getRoles()); + } + + @Test + public void testChangeUsername() throws UserCrudException { + + + final String id = RandomStringUtils.random(5); + Mockito.when(idGen.generateId()).thenReturn(id); + final User u1 = new User("testuser"); + u1.setPassword("password"); + + final User newUser = this.userService.saveNewEntity(u1); + Mockito.when(store.get(User.class, id)).thenReturn(newUser); + + final String uid = newUser.getId(); + + final User update = new User(null, "newname", null, null); + final User updatedUser = this.userService.updateEntity(uid, update); + + assertEquals("newname", updatedUser.getUsername()); + assertEquals(newUser.getId(), updatedUser.getId()); + assertEquals(u1.getRoles(), updatedUser.getRoles()); + assertEquals(u1.getPassword(), updatedUser.getPassword()); + } + + @Test + public void testChangeRoles() throws UserCrudException { + final String id = RandomStringUtils.random(5); + Mockito.when(idGen.generateId()).thenReturn(id); + final User u1 = new User("testuser"); + u1.setPassword("password"); + + final User newUser = this.userService.saveNewEntity(u1); + Mockito.when(store.get(User.class, id)).thenReturn(newUser); + + final String uid = newUser.getId(); + + final User update = + new User(null, null, null, + Collections.singletonList(Role.USER_NAME)); + final User updatedUser = this.userService.updateEntity(uid, update); + + assertTrue(updatedUser.getRoles().stream().anyMatch(r -> r.equals(Role.USER_NAME))); + assertEquals(newUser.getId(), updatedUser.getId()); + assertEquals(u1.getUsername(), updatedUser.getUsername()); + assertEquals(u1.getPassword(), updatedUser.getPassword()); + } + + } diff --git a/user-service/user-model/src/main/java/net/explorviz/security/user/Role.java b/user-service/user-model/src/main/java/net/explorviz/security/user/Role.java index ef329bee..83793e60 100644 --- a/user-service/user-model/src/main/java/net/explorviz/security/user/Role.java +++ b/user-service/user-model/src/main/java/net/explorviz/security/user/Role.java @@ -7,7 +7,9 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; - +/** + * The roles of a user reflect its privileges/permissions. + */ @Type("role") public class Role { @@ -24,27 +26,13 @@ public class Role { */ public static final String USER_NAME = "user"; - public static final Role USER = new Role(USER_NAME); - - - public static final List ROLES = new ArrayList() { /** - * - */ - private static final long serialVersionUID = 1L; + public static final Role USER = new Role(USER_NAME); - { - add(ADMIN); - add(USER); - } }; + public static final List ROLES = new ArrayList<>(); - - /** - * Checks whether a role with the given name exists. - * @param roleName Name of the role to check - * @return {@code true} iff a role with the given name exists - */ - public static boolean exists(String roleName) { - return ROLES.stream().anyMatch(r -> r.getName().equals(roleName)); + static { + ROLES.add(ADMIN); + ROLES.add(USER); } @Id @@ -53,13 +41,23 @@ public static boolean exists(String roleName) { /** * Creates a new roles. */ - private Role(String name) { + private Role(final String name) { this.name = name; } - // Jackson - public Role(){ + public Role() {/* Jackson */ } + + + + /** + * Checks whether a role with the given name exists. + * + * @param roleName Name of the role to check + * @return {@code true} iff a role with the given name exists + */ + public static boolean exists(final String roleName) { + return ROLES.stream().anyMatch(r -> r.getName().equals(roleName)); } @@ -68,7 +66,8 @@ public String getName() { return name; } - @Override public boolean equals(Object o) { + @Override + public boolean equals(final Object o) { if (this == o) { return true; } @@ -77,7 +76,7 @@ public String getName() { return false; } - Role role = (Role) o; + final Role role = (Role) o; return new EqualsBuilder().append(name, role.name).isEquals(); } diff --git a/user-service/user-model/src/main/java/net/explorviz/security/user/User.java b/user-service/user-model/src/main/java/net/explorviz/security/user/User.java index 18ead21a..aaf7d201 100644 --- a/user-service/user-model/src/main/java/net/explorviz/security/user/User.java +++ b/user-service/user-model/src/main/java/net/explorviz/security/user/User.java @@ -111,7 +111,7 @@ public String getBatchId() { return batchId; } - public void setBatchId(String batchId) { + public void setBatchId(final String batchId) { this.batchId = batchId; }