Skip to content

Commit

Permalink
use backend filtering on engagements. - region, type, status, categor… (
Browse files Browse the repository at this point in the history
#182)

* use backend filtering on engagements. - region, type, status, category. some error handling for service outage

* search queries for engagement and customer

* remove commented code
  • Loading branch information
mcanoy authored Apr 29, 2022
1 parent 37089ce commit b5c39d8
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 125 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.redhat.labs.lodestar.model.filter;

import lombok.*;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.QueryParam;
import java.util.*;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class EngagementFilterOptions {

@Parameter(name = "Accept-version", description = "Valid Values are 'v1' or 'v2'. v2 pages results by default. v1 sets per page to 500.")
@HeaderParam(value = "Accept-version")
String apiVersion;

@Parameter(name = "search", description = "Deprecated. search string used to query engagements. allows =, not like, like, not exists, exists")
@QueryParam("search")
@Deprecated
private String search;

@Parameter(name = "q", description = "Free search string. Currently supports Engagement and Customer names.")
@QueryParam("q")
private String q;

@Parameter(description = "sort value. Default Dir to ASC. Ex. field1|DESC,field2,field3|DESC. Always last sort by uuid")
@QueryParam("sortFields")
@DefaultValue("lastUpdate|desc")
private String sortFields;

@Parameter(name = "page", description = "page to be returned. Starts at 1")
@QueryParam("page")
@DefaultValue("1")
private Integer page;

@Parameter(name = "perPage", description = "number of results per page to return")
@QueryParam("perPage")
@DefaultValue("1000")
private Integer perPage;

@Parameter(name = "regions", description = "include only these regions. All regions if empty")
@QueryParam("regions")
private Set<String> regions;

@Parameter(name = "types", description = "include only these types. All types if empty")
@QueryParam("types")
private Set<String> types;

@Parameter(name = "states", description = "include only these states. All states if empty")
@QueryParam("states")
private Set<String> states;

@Parameter(name = "category", description = "find by category")
@QueryParam("category")
private String category;

//Relic
public Set<String> getV2Regions() {
return regions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.ws.rs.core.UriInfo;

import com.redhat.labs.lodestar.model.EngagementUserSummary;
import com.redhat.labs.lodestar.model.filter.EngagementFilterOptions;
import com.redhat.labs.lodestar.service.ParticipantService;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.eclipse.microprofile.openapi.annotations.Operation;
Expand Down Expand Up @@ -57,8 +58,6 @@
public class EngagementResource {
private static final Logger LOGGER = LoggerFactory.getLogger(EngagementResource.class);

private static final String ACCEPT_VERSION_1 = "v1";

public static final String ACCESS_CONTROL_EXPOSE_HEADER = "Access-Control-Expose-Headers";
public static final String LAST_UPDATE_HEADER = "last-update";

Expand Down Expand Up @@ -86,11 +85,9 @@ public class EngagementResource {
@APIResponses(value = { @APIResponse(responseCode = "401", description = "Missing or Invalid JWT"),
@APIResponse(responseCode = "200", description = "A list or empty list of engagement resources returned") })
@Operation(summary = "Returns all engagement resources from the database. Can be empty list if none found.")
public Response getAll(@Context UriInfo uriInfo, @BeanParam ListFilterOptions filterOptions) {

// create one page with many results for v1
setDefaultPagingFilterOptions(filterOptions);
public Response getAll(@Context UriInfo uriInfo, @BeanParam EngagementFilterOptions filterOptions) {

LOGGER.debug("sort fields {}", filterOptions.getSortFields());
return engagementService.getEngagementsPaged(filterOptions);
}

Expand Down Expand Up @@ -188,7 +185,7 @@ public Response get(@PathParam("id") String uuid, @BeanParam FilterOptions filte
* GET - Queries
*/

// Not sure if this one is being used currently
// Not sure if this one is being used currently - should be covered by regular get. no?
@GET
@Path("/state/{state}")
@SecurityRequirement(name = "jwt")
Expand All @@ -198,19 +195,7 @@ public Response get(@PathParam("id") String uuid, @BeanParam FilterOptions filte
public Response getByState(@Context UriInfo uriInfo, @PathParam("state") String state,
@Parameter(name = "start", required = true, description = "start date of range") @NotBlank @QueryParam("start") String start,
@Parameter(name = "end", required = true, description = "end date of range") @NotBlank @QueryParam("end") String end,
@BeanParam ListFilterOptions filterOptions) {

// set defaults for paging if not already set
setDefaultPagingFilterOptions(filterOptions);

// set state parameter
filterOptions.addEqualsSearchCriteria("state", state);

// set start parameter
filterOptions.addEqualsSearchCriteria("start", start);

// set end parameter
filterOptions.addEqualsSearchCriteria("end", end);
@BeanParam EngagementFilterOptions filterOptions) {

PagedEngagementResults page = new PagedEngagementResults(); //TODO engagementService.getEngagementsPaged(filterOptions);
ResponseBuilder builder = Response.ok(page.getResults()).links(page.getLinks(uriInfo.getAbsolutePathBuilder()));
Expand All @@ -233,9 +218,6 @@ public Response getUserSummary(@QueryParam("search") String search) {
for (String param : params) {
String[] keyValues = param.split("=");

if(keyValues.length == 0) {
Response.status(Response.Status.BAD_REQUEST).build();
}
if (keyValues[0].equals("engagement_region")) {
String[] regionsArray = keyValues[1].split(",");
regions = Arrays.asList(regionsArray);
Expand Down Expand Up @@ -320,33 +302,6 @@ public Response post(@Valid Engagement engagement, @Context UriInfo uriInfo) {

}

@PUT
@Deprecated
@SecurityRequirement(name = "jwt")
@Path("/customers/{customerName}/projects/{projectName}")
@APIResponses(value = { @APIResponse(responseCode = "401", description = "Missing or Invalid JWT"),
@APIResponse(responseCode = "403", description = "Not authorized for engagement type"),
@APIResponse(responseCode = "404", description = "Engagement resource not found to update"),
@APIResponse(responseCode = "200", description = "Engagement updated in the database") })
@Operation(deprecated = true, summary = "Updates the engagement resource in the database.")
public Response put(@PathParam("customerName") String customerName, @PathParam("projectName") String projectName,
@Valid Engagement engagement) {

LOGGER.warn("Deprecated put method used /customers/{}/projects/{}", customerName, projectName);

boolean writer = jwtUtils.isAllowedToWriteEngagement(jwt, configService.getPermission(engagement.getType()));
if(!writer) {
return forbiddenResponse(engagement.getType());
}

// pull user info from token
engagement.setLastUpdateByName(jwtUtils.getUsernameFromToken(jwt));
engagement.setLastUpdateByEmail(jwtUtils.getUserEmailFromToken(jwt));

return Response.ok(engagementService.update(engagement)).build();

}

@PUT
@SecurityRequirement(name = "jwt")
@Path("/{id}")
Expand Down Expand Up @@ -420,21 +375,11 @@ public Response delete(@PathParam("id") String uuid) {

engagementService.deleteEngagement(uuid);
return Response.accepted().build();

}

private Response forbiddenResponse(String type) {
String message = String.format("{\"message\": \"You cannot modify %s engagements\"}", type);
return Response.status(403).entity(message).build();
}

private void setDefaultPagingFilterOptions(ListFilterOptions options) {

boolean isV1 = null == options.getApiVersion() || ACCEPT_VERSION_1.equals(options.getApiVersion());

options.setPage(options.getPage().orElse(1));
options.setPerPage(options.getPerPage().orElse(isV1 ? 500 : 20));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@RequestScoped
Expand Down Expand Up @@ -51,45 +53,45 @@ public Response refresh(
@Parameter(description = "Refresh hosting environments") @QueryParam("hosting") boolean refreshHosting,
@Parameter(description = "Refresh engagements") @QueryParam("engagements") boolean refreshEngagements) {

boolean didPickSomething = false;
List<String> refreshed = new ArrayList<>();

if (refreshEngagements) { //Engagements done first since all others are predicated on this content.
//Engagements will be synchronous
engagementService.refresh(uuids);
didPickSomething = true;
refreshed.add("engagements");
}

if(refreshHosting) {
eventBus.publish(EventType.RELOAD_HOSTING_EVENT_ADDRESS, EventType.RELOAD_HOSTING_EVENT_ADDRESS);
didPickSomething = true;
refreshed.add("hosting");
}

if (refreshActivity) {
eventBus.publish(EventType.RELOAD_ACTIVITY_EVENT_ADDRESS, EventType.RELOAD_ACTIVITY_EVENT_ADDRESS);
didPickSomething = true;
refreshed.add("activity");
}

if (refreshParticipants) {
eventBus.publish(EventType.RELOAD_PARTICIPANTS_EVENT_ADDRESS,
EventType.RELOAD_PARTICIPANTS_EVENT_ADDRESS);
didPickSomething = true;
refreshed.add("participants");
}

if (refreshArtifacts) {
eventBus.publish(EventType.RELOAD_ARTIFACTS_EVENT_ADDRESS, EventType.RELOAD_ARTIFACTS_EVENT_ADDRESS);
didPickSomething = true;
refreshed.add("artifacts");
}

if (refreshStatus) {
eventBus.publish(EventType.RELOAD_ENGAGEMENT_STATUS_EVENT_ADDRESS, EventType.RELOAD_ENGAGEMENT_STATUS_EVENT_ADDRESS);
didPickSomething = true;
refreshed.add("status");
}

if (didPickSomething) {
return Response.accepted().build();
if (refreshed.isEmpty()) {
return Response.status(400).entity("{ \"message\" : \"No refresh source was selected\" }").build();
}

return Response.status(400).entity("{ \"message\" : \"No refresh source was selected\" }").build();
return Response.accepted().entity(refreshed).build();

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
public interface EngagementApiClient {

@GET
Response getEngagements(@QueryParam("page") int page, @QueryParam("pageSize") int pageSize, @QueryParam("region") Set<String> region);
Response getEngagements(@QueryParam("page") int page, @QueryParam("pageSize") int pageSize, @QueryParam("region") Set<String> region,
@QueryParam("types") Set<String> types, @QueryParam("inStates") Set<String> states, @QueryParam("q") String search,
@QueryParam("category") String category, @QueryParam("sort") String sort);

//TODO support time shifting
@GET
Expand All @@ -26,7 +28,8 @@ public interface EngagementApiClient {

@GET
@Path("category/{category}")
List<Engagement> getEngagementsWithCategory(@PathParam("category") String category);
List<Engagement> getEngagementsWithCategory(@QueryParam("page") int page, @QueryParam("pageSize") int pageSize, @QueryParam("region") Set<String> region,
@QueryParam("types") Set<String> types, @QueryParam("inStates") Set<String> states, @PathParam("category") String category, @QueryParam("sort") String sort);

@GET
@Path("{uuid}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.redhat.labs.lodestar.service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.*;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
Expand Down Expand Up @@ -43,9 +40,15 @@ public List<Artifact> getArtifacts(String engagementUuid) {
ArtifactOptions options = ArtifactOptions.builder().page(0).pageSize(1000)
.engagementUuid(engagementUuid).build();

Response response = artifactRestClient.getArtifacts(options);

return response.readEntity(new GenericType<>(){});
try {
return artifactRestClient.getArtifacts(options).readEntity(new GenericType<>(){});
} catch (WebApplicationException wex) {
if(wex.getResponse().getStatus() >= 500) {
LOGGER.error("Artifact Server error ({}) from hosting env for euuid {}", wex.getResponse().getStatus(), engagementUuid);
return Collections.EMPTY_LIST;
}
throw wex;
}
}

public Response getArtifacts(ListFilterOptions filterOptions, String engagementUuid, String type, List<String> region, boolean dashboardView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.redhat.labs.lodestar.model.*;
import com.redhat.labs.lodestar.model.Engagement.EngagementState;
import com.redhat.labs.lodestar.model.filter.EngagementFilterOptions;
import com.redhat.labs.lodestar.model.filter.ListFilterOptions;
import com.redhat.labs.lodestar.model.pagination.PagedEngagementResults;
import com.redhat.labs.lodestar.rest.client.CategoryApiClient;
Expand All @@ -19,6 +20,7 @@

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
Expand Down Expand Up @@ -116,6 +118,8 @@ private Status getStatus(String uuid) {
} else {
LOGGER.error("Exception occurred retrieving status for engagement {}", uuid);
}
} catch (ProcessingException pe) {
LOGGER.error("Cannot connect to lodestar-engagement-status for engagement {}", uuid, pe);
}

return null;
Expand Down Expand Up @@ -321,51 +325,37 @@ public Map<EngagementState, Integer> getEngagementCountByStatus(Instant currentT
* Returns a {@link PagedEngagementResults} of {@link Engagement} that matches
* the {@link ListFilterOptions}.
*
* @param listFilterOptions
* @param filter
* @return ?
*/
public Response getEngagementsPaged(ListFilterOptions listFilterOptions) {
public Response getEngagementsPaged(EngagementFilterOptions filter) {
//TODO hacking for v1
//TODO should probably better align last activity field on engagement object so that
// we can just filter on engagement instead of hitting the activity service
String sort = listFilterOptions.getSortFields().orElse("");
int pageSize = listFilterOptions.getPerPage().orElse(5);
String sort = filter.getSortFields();
int pageSize = filter.getPerPage();

if(pageSize == 5 && sort.equals("last_update")) {
List<String> activity = activityService.getLatestActivity(0,5, listFilterOptions.getV2Regions());
List<String> activity = activityService.getLatestActivity(0,5, filter.getV2Regions());
List<Engagement> engagements = activity.stream().map(this::getEngagement).collect(Collectors.toList());
return Response.ok(engagements).build();
}

int page = listFilterOptions.getPage().orElse(1) - 1;
pageSize = listFilterOptions.getPerPage().orElse(1000);
int page = filter.getPage() - 1;
pageSize = filter.getPerPage();

Response response = engagementApiClient.getEngagements(page, pageSize, listFilterOptions.getV2Regions());
Response response = engagementApiClient.getEngagements(page, pageSize, filter.getRegions(), filter.getTypes(), filter.getStates(), filter.getQ(), filter.getCategory(), sort);
List<Engagement> engagements = response.readEntity(new GenericType<>(){});
String total = response.getHeaderString("x-total-engagements");

Map<String, String> engagementOptions = configService.getEngagementOptions();

//TODO this loop is to allow frontend to change after v2 deployment.
// FE should use participant, artifact count field, and categories (string version)
for(Engagement e : engagements) {

for(int i=0; i<e.getParticipantCount(); i++) {
e.addParticipant(EngagementUser.builder().email(String.valueOf(i)).build());
}

for(int i=0; i<e.getArtifactCount(); i++) {
e.addArtifact(Artifact.builder().type("temp").build());
}

if(e.getCategoriesV2() != null) {
for (String cat : e.getCategoriesV2()) {
e.addCategory(cat);
}
}

e.setPrettyType(engagementOptions.containsKey(e.getType()) ? engagementOptions.get(e.getType()) : e.getType());
}
return Response.ok(engagements).header("x-total-engagements", response.getHeaderString("x-total-engagements")).build();
return Response.ok(engagements).header("x-total-engagements", total).build();
}

/**
Expand Down
Loading

0 comments on commit b5c39d8

Please sign in to comment.