package de.hftstuttgart.unifiedticketing.systems.github; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import de.hftstuttgart.unifiedticketing.core.Filter; import de.hftstuttgart.unifiedticketing.core.Logging; import de.hftstuttgart.unifiedticketing.core.TicketSystem; import de.hftstuttgart.unifiedticketing.exceptions.*; import okhttp3.*; import java.io.IOException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; public class GithubFilter extends Filter { private final static Logger logger = Logging.getLogger(GithubFilter.class.getName()); protected final GithubTicketSystem parent; protected GithubFilter(GithubTicketSystem parent) { this.parent = parent; } protected OkHttpClient getHttpClient() { return new OkHttpClient(); } @Override public List get() { ObjectMapper mapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Request.Builder requestBuilder = new Request.Builder() .url(parent.baseUrl) .addHeader("accept", parent.acceptHeader) .get(); if (parent.username != null && parent.apiKey != null) { requestBuilder.addHeader("Authorization", Credentials.basic(parent.username, parent.apiKey)); logger.log(Level.FINEST, "added token authentication header"); } HttpUrl.Builder urlBuilder = requestBuilder.build().url().newBuilder(); for (Map.Entry mapEntry : setFilters.entrySet()) { String f = mapEntry.getKey(); Object v = mapEntry.getValue(); try { if (f.equals(FilterNames.ASSIGNEEID.name())) { logger.log(Level.WARNING, "assignee-id check only possible after request |" + " Filter: " + FilterNames.ASSIGNEEID.name()); } else if (f.equals(FilterNames.ASSIGNEENAME.name())) { Set names = (Set) v; if (names.size() > 0) { urlBuilder.addQueryParameter("assignee", names.stream() .findFirst() .get()); if (names.size() > 1) { logger.log(Level.WARNING, "assignee-name filter natively only for one name supported"); } } } else if (f.equals(FilterNames.DESCRIPTION_CONTAIN.name())) { logger.log(Level.FINE, "Description contain check only possible after request |" + " Filter: " + FilterNames.DESCRIPTION_CONTAIN.name()); } else if (f.equals(FilterNames.DESCRIPTION_MATCH.name())) { logger.log(Level.FINE, "Regex matching only possible after request |" + " Filter: " + FilterNames.DESCRIPTION_MATCH.name()); } else if (f.equals(FilterNames.IDS.name())) { logger.log(Level.FINE, "Ticket id matching only possible after request |" + " Filter: " + FilterNames.IDS.name()); } else if (f.equals(FilterNames.LABELS.name())) { urlBuilder.addQueryParameter("labels", ((Set) v).stream() .reduce((l1, l2) -> l1 + "," + l2) .orElse("")); } else if (f.equals(FilterNames.PAGE.name())) { urlBuilder.addQueryParameter("page", String.valueOf(v)); } else if (f.equals(FilterNames.PAGINATION.name())) { urlBuilder.addQueryParameter("per_page", String.valueOf(v)); } else if (f.equals(FilterNames.OPEN.name())) { urlBuilder.addQueryParameter("state", ((boolean) v) ? "open" : "closed"); } else if (f.equals(FilterNames.TITLE_CONTAINS.name())) { logger.log(Level.FINE, "title contain check only possible after request |" + " Filter: " + FilterNames.TITLE_CONTAINS.name()); } else if (f.equals(FilterNames.TITLE_MATCH.name())) { logger.log(Level.FINE, "Regex matching only possible after request |" + " Filter: " + FilterNames.TITLE_MATCH.name()); } else { logger.log(Level.WARNING, String.format("unrecognized filter key: %s", f)); } } catch (ClassCastException e) { logger.log(Level.SEVERE, "Filter with key " + f + " unexpectedly had type " + v.getClass().getName()); if (!this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) { throw new AssertionException(e); } } } requestBuilder.url(urlBuilder.build()); OkHttpClient client = getHttpClient(); Response response; try { Request request = requestBuilder.build(); logger.log(Level.FINEST, String.format( "created request:\n%s", (this.parent.apiKey != null) ? request.toString().replace(this.parent.apiKey, "SECRET") : request.toString() )); response = client.newCall(request).execute(); } catch (IOException e) { logger.log(Level.SEVERE, String.format("get request FAILED with: %s", e.getMessage())); if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null; else throw new HttpReqeustException(e); } if (response.code() >= 400) { logger.log(Level.SEVERE, String.format( "request failed with response code %d", response.code() )); if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null; else throw new HttpResponseException( String.format("ticket query failed, error response code: %d", response.code()), response.code()); } logger.log(Level.FINEST, "response received\n"); ResponseBody responseBody; responseBody = response.body(); if (responseBody == null) { logger.log(Level.SEVERE, "query didn't deliver a body in response"); if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null; else throw new HttpResponseException("ticket query failed, no response body", response.code()); } List tr; try { tr = mapper.readValue(responseBody.bytes(), new TypeReference>(){}); logger.log(Level.FINER, "parsed response body to ticketResponse list instance"); logger.log(Level.FINEST, String.format("found %d items pre post-filter", tr.size())); } catch (IOException e) { logger.log(Level.SEVERE, String.format("parsing query response FAILED with: %s", e.getMessage())); if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null; else throw new DeserializationException(e); } lastReceivedItemCount = tr.size(); logger.log(Level.FINER, "starting query post filter"); Stream ticketStream = tr.stream(); for (Map.Entry entry : setFilters.entrySet()) { String f = entry.getKey(); Object v = entry.getValue(); try { if (f.equals(FilterNames.ASSIGNEEID.name())) { ticketStream = ticketStream.filter(t -> t.assignees.stream() .map(a -> String.valueOf(a.id)) .collect(Collectors.toSet()) .equals((Set) v)); } else if (f.equals(FilterNames.ASSIGNEENAME.name()) && ((Set) v).size() > 1) { ticketStream = ticketStream.filter(t -> t.assignees.stream() .map(a -> a.login) .collect(Collectors.toSet()) .equals((Set) v)); } else if (f.equals(FilterNames.DESCRIPTION_CONTAIN)) { ticketStream = ticketStream.filter(t -> t.body .toLowerCase() .contains(((String) v).toLowerCase())); } else if (f.equals(FilterNames.DESCRIPTION_MATCH.name())) { ticketStream = ticketStream.filter(t -> t.body.matches((String) v)); } else if (f.equals(FilterNames.TITLE_CONTAINS.name())) { ticketStream = ticketStream.filter(t -> t.title .toLowerCase() .contains(((String) v).toLowerCase())); } else if (f.equals(FilterNames.TITLE_MATCH.name())) { ticketStream = ticketStream.filter(t -> t.title.matches((String) v)); } } catch (ClassCastException e) { logger.log(Level.SEVERE, "Filter with key " + f + " unexpectedly had type " + v.getClass().getName()); } } logger.log(Level.FINER, "post-filter finished"); List ret = ticketStream.map(t -> GithubTicket.fromTicketResponse(parent, t)) .collect(Collectors.toList()); logger.log(Level.FINEST, String.format("remaining items: %d", ret.size())); return ret; } }