package de.hftstuttgart.unifiedticketing.exceptions;
public class DeserializationException extends UnifiedticketingException
{
public DeserializationException() { super(); }
public DeserializationException(String msg)
{
super(msg);
}
public DeserializationException(String msg, Throwable cause)
{
super(msg, cause);
}
public DeserializationException(Throwable suppressed) { super(suppressed); }
}
package de.hftstuttgart.unifiedticketing.exceptions;
public class HttpReqeustException extends UnifiedticketingException
{
public HttpReqeustException() { super(); }
public HttpReqeustException(String msg)
{
super(msg);
}
public HttpReqeustException(String msg, Throwable casue)
{
super(msg, casue);
}
public HttpReqeustException(Throwable suppressed) { super(suppressed); }
}
package de.hftstuttgart.unifiedticketing.exceptions;
public class HttpResponseException extends UnifiedticketingException
{
public final int status;
public HttpResponseException(int status)
{
super();
this.status = status;
}
public HttpResponseException(String msg, int status)
{
super(msg);
this.status = status;
}
public HttpResponseException(String msg, Throwable cause, int status)
{
super(msg, cause);
this.status = status;
}
public HttpResponseException(Throwable suppressed, int status)
{
super(suppressed);
this.status = status;
}
}
package de.hftstuttgart.unifiedticketing.exceptions;
public class SerializationException extends UnifiedticketingException
{
public SerializationException() { super(); }
public SerializationException(String msg)
{
super(msg);
}
public SerializationException(String msg, Throwable cause)
{
super(msg, cause);
}
public SerializationException(Throwable suppressed) { super(suppressed); }
}
package de.hftstuttgart.unifiedticketing.exceptions;
public class UnifiedticketingException extends RuntimeException
{
public UnifiedticketingException() { super(); }
public UnifiedticketingException(String msg)
{
super(msg);
}
public UnifiedticketingException(String msg, Throwable cause)
{
super(msg, cause);
}
public UnifiedticketingException(Throwable suppressed)
{
this(suppressed.getMessage());
this.addSuppressed(suppressed);
}
}
package de.hftstuttgart.unifiedticketing.exceptions;
public class UnsupportedFunctionException extends UnifiedticketingException
{
public UnsupportedFunctionException() { super(); }
public UnsupportedFunctionException(String msg)
{
super(msg);
}
public UnsupportedFunctionException(String msg, Throwable cause)
{
super(msg, cause);
}
public UnsupportedFunctionException(Throwable suppressed) { super(suppressed); }
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
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.AssertionException;
import de.hftstuttgart.unifiedticketing.exceptions.DeserializationException;
import de.hftstuttgart.unifiedticketing.exceptions.HttpReqeustException;
import de.hftstuttgart.unifiedticketing.exceptions.HttpResponseException;
import okhttp3.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GitlabFilter extends Filter<GitlabTicket, GitlabFilter>
{
private static Logger logger = Logging.getLogger(GitlabFilter.class.getName());
protected final GitlabTicketSystem parent;
protected GitlabFilter(GitlabTicketSystem parent)
{
this.parent = parent;
}
protected OkHttpClient getHttpClient() { return new OkHttpClient(); }
/**
* this builds up a request with as much of the given constraints directed to the
* gitlab api. Everything not possible through the api will be filtered afterwards in java
* @return ticket list matching given constraints
*/
@Override
public List<GitlabTicket> get()
{
OkHttpClient client;
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Request.Builder requestBuilder = new Request.Builder()
.url(String.format("%s/issues", parent.baseUrl))
.get();
if (parent.apiKey != null)
{
requestBuilder.addHeader("PRIVATE-TOKEN", parent.apiKey);
}
HttpUrl.Builder urlBuilder = requestBuilder.build().url().newBuilder();
for (Map.Entry<String, Object> mapEntry : setFilters.entrySet())
{
String f = mapEntry.getKey();
Object v = mapEntry.getValue();
try
{
if (f.equals(FilterNames.ASSIGNEEID.name()))
{
((Set<String>) v).stream()
.findFirst()
.ifPresent(value -> urlBuilder.addQueryParameter("assignee_id", value));
} else if (f.equals(FilterNames.ASSIGNEENAME.name()))
{
urlBuilder.addQueryParameter("assignee_username", ((Set<String>) v).stream()
.reduce((u1, u2) -> u1 + "," + u2)
.orElse(""));
} else if (f.equals(FilterNames.DESCRIPTION_CONTAIN.name()))
{
urlBuilder.addQueryParameter("search", (String) v);
} 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()))
{
urlBuilder.addQueryParameter("iids", ((Set<String>) v).stream()
.reduce((id1, id2) -> id1 + "," + id2)
.orElse(""));
} else if (f.equals(FilterNames.LABELS.name()))
{
urlBuilder.addQueryParameter("labels", ((Set<String>) 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) ? "opened" : "closed");
} else if (f.equals(FilterNames.TITLE_CONTAINS.name()))
{
urlBuilder.addQueryParameter("search", (String) v);
} 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());
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<GitlabTicketResponse> tr;
try
{
tr = mapper.readValue(responseBody.bytes(), new TypeReference<List<GitlabTicketResponse>>(){});
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);
}
logger.log(Level.FINER, "starting query post filter");
Stream<GitlabTicketResponse> ticketStream = tr.stream();
for (Map.Entry<String, Object> entry : setFilters.entrySet())
{
String f = entry.getKey();
Object v = entry.getValue();
try
{
if (f.equals(FilterNames.DESCRIPTION_MATCH.name()))
{
ticketStream = ticketStream.filter(t -> t.description.matches((String) v));
}
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<GitlabTicket> ret = ticketStream.map(t -> GitlabTicket.fromTicketResponse(parent, t))
.collect(Collectors.toList());
logger.log(Level.FINEST, String.format("remaining items: %d", ret.size()));
return ret;
}
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import de.hftstuttgart.unifiedticketing.core.Logging;
import de.hftstuttgart.unifiedticketing.core.Ticket;
import de.hftstuttgart.unifiedticketing.core.TicketSystem;
import de.hftstuttgart.unifiedticketing.exceptions.*;
import de.hftstuttgart.unifiedticketing.exceptions.*;
import okhttp3.*;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class GitlabTicket extends Ticket<GitlabTicketSystem, GitlabTicket>
{
private static Logger logger = Logging.getLogger(GitlabTicket.class.getName());
protected GitlabTicket(GitlabTicketSystem parent)
{
super(parent);
}
/**
* method to parse a library conform ticket instance from the JSON response of gitlab api
* @param parent ticket system instance to be placed as parent
* @param response api response
* @return library conform ticket instance
*/
protected static GitlabTicket fromTicketResponse(GitlabTicketSystem parent, GitlabTicketResponse response)
{
GitlabTicket ret = new GitlabTicket(parent);
ret.description = response.description;
ret.id = String.valueOf(response.iid);
if (response.labels != null)
{
ret.labels = response.labels;
}
ret.open = (response.state != null)
? !response.state.equalsIgnoreCase("closed")
: true;
ret.title = response.title;
if (response.assignees != null)
{
ret.assignees = response.assignees.stream()
.map(a -> new GitlabTicketAssignee(a.id, a.name, a.username))
.collect(Collectors.toCollection(HashSet::new));
}
return ret;
}
/**
* adds assignee by it's user id
*
* @param userId gitlab user id parsable as integer
* @return this ticket
* @throws AssertionException if {@code userId} is not integer parsable
*/
@Override
public GitlabTicket addAssignee(String userId)
{
try
{
return this.addAssignee(Integer.parseInt(userId));
} catch (NumberFormatException e)
{
logger.log(Level.SEVERE, String.format("%s is not applicable for a Gitlab user id", userId));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return this;
else throw new AssertionException(e);
}
}
/**
* adds assignee by it's user id
*
* @param userId gitlab user id
* @return this ticket
*/
public GitlabTicket addAssignee(int userId)
{
this.assignees.add(new GitlabTicketAssignee(userId, null, null));
this.updatedFields.add(FieldNames.ASSIGNEES.name());
return this;
}
/**
* removes assignee by it's user id
*
* @param userId gitlab user id parsable as integer
* @return this ticket
* @throws AssertionException if {@code userId} is not integer parsable
*/
@Override
public GitlabTicket removeAssignee(String userId)
{
int id;
try
{
return this.removeAssignee(Integer.parseInt(userId));
} catch (NumberFormatException e)
{
logger.log(Level.SEVERE, String.format("%s is not applicable for a Gitlab user id", userId));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return this;
else throw new AssertionException(e);
}
}
/**
* removes assignee by it's user id
*
* @param userId gitlab user id parsable as integer
* @return this ticket
*/
public GitlabTicket removeAssignee(int userId)
{
this.assignees.removeIf(a -> Objects.equals(a.id, String.valueOf(userId)));
this.updatedFields.add(FieldNames.ASSIGNEES.name());
return this;
}
protected OkHttpClient getHttpClient()
{
return new OkHttpClient();
}
/**
* checks for changed fields and serializes them into a PUT request
* to update this ticket instance.
* @return newly received ticket data as new {@link GitlabTicket instance}
*/
@Override
public GitlabTicket save()
{
if (updatedFields.size() == 0)
{
logger.info("No changed fields, no save required");
return this;
}
OkHttpClient client = this.getHttpClient();
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
logger.log(Level.FINEST, String.format(
"[Ticket %s] preparing body for update request",
this.id
));
ObjectNode body = mapper.createObjectNode();
for (String name : this.updatedFields)
{
if (FieldNames.ASSIGNEES.name().equals(name))
{
ArrayNode ids = body.putArray("assignee_ids");
try
{
this.assignees.forEach(a -> ids.add(Integer.parseInt(a.id)));
} catch (NumberFormatException e)
{
logger.log(Level.SEVERE, "parsing assignee id failed on serialization");
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR))
{
body.remove("assignee_ids");
continue;
}
else
{
throw new SerializationException(e);
}
}
}
else if (FieldNames.DESCRIPTION.name().equals(name))
{
body.put("description", this.description);
}
else if (FieldNames.LABELS.name().equals(name))
{
body.put("labels", this.labels.stream().reduce((l1, l2) -> l1 + "," + l2).orElse(""));
}
else if (FieldNames.OPEN.name().equals(name))
{
body.put("state_event", (this.open) ? "reopen" : "close");
}
else if (FieldNames.TITLE.name().equals(name))
{
body.put("title", this.title);
}
else
{
logger.log(Level.WARNING, String.format(
"[Ticket %s] unknown field %s will be ignored from update",
this.id,
name
));
continue;
}
logger.log(Level.FINEST, String.format(
"[Ticket %s] %s added to update",
this.id,
name
));
}
logger.log(Level.FINEST, String.format(
"[Ticket %s] request body for update prepared",
this.id
));
Request.Builder builder = new Request.Builder()
.url(String.format("%s/issues/%s", this.parent.baseUrl, this.id));
try
{
logger.log(Level.FINEST, String.format(
"[Ticket %s] serializing update request body",
this.id
));
String bodyJson = mapper.writeValueAsString(body);
logger.log(Level.FINEST, String.format(
"[Ticket %s] serialized JSON:\n%s",
this.id,
bodyJson
));
builder
.put(RequestBody.create(bodyJson, MediaType.get("application/json")));
} catch (JsonProcessingException e)
{
logger.log(Level.SEVERE, String.format(
"[Ticket %s] serializing update request body FAILED!",
this.id
));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new SerializationException(e);
}
if (parent.apiKey != null)
{
builder.addHeader("PRIVATE-TOKEN", parent.apiKey);
logger.log(Level.FINEST, String.format(
"[Ticket %s] added token authentication header",
this.id
));
}
Response response;
try
{
Request request = builder.build();
logger.log(Level.FINEST, String.format(
"[Ticket %s] created request:\n%s",
this.id,
(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(
"[Ticket %s] update request FAILED",
this.id
));
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(
"[Ticket %s] update request failed with response code %d",
this.id,
response.code()
));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new HttpResponseException(
String.format("ticket save failed, error response code: %d", response.code()),
response.code());
}
logger.log(Level.FINEST, String.format(
"[Ticket %s] response received",
this.id
));
ResponseBody responseBody;
responseBody = response.body();
if (responseBody == null)
{
logger.log(Level.SEVERE, String.format(
"[Ticket %s] update request didn't deliver a body in response",
this.id
));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new HttpResponseException("ticket save failed, no response body", response.code());
}
GitlabTicketResponse ticketResponse;
try
{
ticketResponse = mapper.readValue(responseBody.bytes(), GitlabTicketResponse.class);
logger.log(Level.FINEST, String.format(
"[Ticket %s] parsed response body to ticketResponse instance",
this.id
));
} catch (IOException e)
{
logger.log(Level.SEVERE, String.format("parsing update response FAILED with: %s", e.getMessage()));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new DeserializationException(e);
}
this.updatedFields.clear();
logger.log(Level.FINEST, String.format(
"[Ticket %s] update mark state reset",
this.id
));
return GitlabTicket.fromTicketResponse(this.parent, ticketResponse);
}
/**
* compares a given Object to this one, including all data fields.
* The normal {@link #equals(Object)} method does only compare Ticket Type and id,
* but not the data fields like this one.
* @param o Object to compare to this
* @return true if the given object is equal in terms of type, id and content to this
*/
public boolean deepEquals(Object o)
{
if (!equals(o)) return false;
GitlabTicket t = (GitlabTicket) o;
return Objects.equals(title, t.title)
&& Objects.equals(description, t.description)
&& Objects.equals(labels, t.labels)
&& Objects.equals(assignees, t.assignees);
}
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
import de.hftstuttgart.unifiedticketing.core.TicketAssignee;
public class GitlabTicketAssignee extends TicketAssignee
{
protected GitlabTicketAssignee(int id, String fullName, String username)
{
super(null, String.valueOf(id), fullName, username);
}
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import de.hftstuttgart.unifiedticketing.core.Logging;
import de.hftstuttgart.unifiedticketing.core.TicketBuilder;
import de.hftstuttgart.unifiedticketing.core.TicketSystem;
import de.hftstuttgart.unifiedticketing.exceptions.*;
import de.hftstuttgart.unifiedticketing.exceptions.*;
import okhttp3.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class GitlabTicketBuilder extends TicketBuilder<GitlabTicketBuilder, GitlabTicket, GitlabTicketSystem>
{
private static Logger logger = Logging.getLogger(GitlabTicketBuilder.class.getName());
protected GitlabTicketBuilder(GitlabTicketSystem parent) {
super(parent);
}
@Override
public GitlabTicketBuilder assignees(String... identifiers)
{
try
{
Arrays.stream(identifiers)
.forEach(Integer::parseInt);
} catch (NumberFormatException e)
{
logger.log(Level.SEVERE, String.format("not as integer parsable assignee id encountered!"));
throw new AssertionException(e);
}
return super.assignees(identifiers);
}
public GitlabTicketBuilder assignees(int... identifiers)
{
return super.assignees(Arrays.stream(identifiers)
.mapToObj(String::valueOf)
.toArray(String[]::new));
}
protected OkHttpClient getHttpClient() { return new OkHttpClient(); }
/**
* creates a new ticket with the collected data and retrieves a new ticket instance from
* the api response
* @return newly created Ticket as {@link GitlabTicket} instance
*/
@Override
public GitlabTicket create()
{
logger.log(Level.FINEST, "starting Ticket creation from builder data");
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectNode body = mapper.createObjectNode();
if (this.title == null)
{
logger.log(Level.SEVERE, "mandatory field title not set before building Ticket");
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new AssertionException("GitLab Tickets need at-least a title");
}
body.put("title", this.title);
logger.log(Level.FINEST, "title set");
if (this.assignees != null)
{
ArrayNode ids = body.putArray("assignee_ids");
assignees
.stream()
.mapToInt(Integer::parseInt)
.forEach(ids::add);
logger.log(Level.FINEST, "assignees set");
}
if (this.description != null)
{
body.put("description", this.description);
logger.log(Level.FINEST, "description set");
}
if (this.labels != null)
{
body.put("labels", this.labels.stream()
.reduce((l1, l2) -> l1 + "," + l2)
.orElse(""));
logger.log(Level.FINEST, "labels set");
}
String jsonBody;
try
{
jsonBody = mapper.writeValueAsString(body);
logger.log(Level.FINER, String.format(
"body for new Ticket serialized as json:\n%s",
jsonBody
));
}
catch (JsonProcessingException e)
{
logger.log(Level.SEVERE, "json serialization FAILED");
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new SerializationException(e);
}
OkHttpClient client = getHttpClient();
Request.Builder builder = new Request.Builder()
.url(String.format("%s/issues", parent.baseUrl));
builder.post(RequestBody.create(jsonBody, MediaType.get("application/json")));
if (this.parent.apiKey != null)
{
builder.addHeader("PRIVATE-TOKEN", this.parent.apiKey);
logger.log(Level.FINEST, "added token authentication header");
}
Response response;
try
{
Request request = builder.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, "create request FAILED");
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(
"create 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 creation 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, "create request didn't deliver a body in response");
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new HttpResponseException("ticket creation failed, no response body", response.code());
}
GitlabTicketResponse ticketResponse;
try
{
ticketResponse = mapper.readValue(responseBody.bytes(), GitlabTicketResponse.class);
logger.log(Level.FINEST, "parsed response body to ticketResponse instance");
} catch (IOException e)
{
logger.log(Level.SEVERE, String.format("parsing update response FAILED with: %s", e.getMessage()));
if (this.parent.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR)) return null;
else throw new DeserializationException(e);
}
return GitlabTicket.fromTicketResponse(this.parent, ticketResponse);
}
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
import java.util.List;
import java.util.Set;
public class GitlabTicketResponse
{
protected GitlabTicketResponse() { }
public List<Assignee> assignees;
public String description;
public int iid;
public Set<String> labels;
public String state;
public String title;
protected static class Assignee
{
public int id;
public String name;
public String username;
}
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
import de.hftstuttgart.unifiedticketing.core.Logging;
import de.hftstuttgart.unifiedticketing.core.TicketSystem;
import de.hftstuttgart.unifiedticketing.exceptions.AssertionException;
import de.hftstuttgart.unifiedticketing.exceptions.UnifiedticketingException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GitlabTicketSystem extends TicketSystem<GitlabTicket, GitlabTicketSystem, GitlabTicketBuilder, GitlabFilter>
{
private static Logger logger = Logging.getLogger(TicketSystem.class.getName());
/**
* creates a new instance of this class from an uri.<br/>
* <br/>
* <b>Attention:</b> This method should not be called from the enduser!<br/>
* Instead call the same method from {@link TicketSystem}.<br/>
* To call this method directly, the uri has to be shortend by the "unifiedticketing:gitlab:" part.
* @param uri gitlab specific part of full uri
* @return new gitlab ticket instance
*/
public static GitlabTicketSystem fromUri(String uri)
{
Matcher matcher = Pattern.compile("^((http|https):\\/\\/)?(.*(:[0-9]+.*)?):([0-9]+)(:(.*))?$").matcher(uri);
if (!matcher.matches())
{
logger.log(Level.SEVERE, "uri didn't match regex");
throw new AssertionException("uri didn't match regex");
}
GitlabTicketSystemBuilder builder = new GitlabTicketSystemBuilder();
if (matcher.group(2) != null && matcher.group(2).length() > 0)
{
if (matcher.group(2).equalsIgnoreCase("http")) builder.withHttp();
else builder.withHttps();
}
if (matcher.group(3) == null || matcher.group(3).length() == 0)
{
logger.log(Level.SEVERE, "no base url identified in uri");
throw new AssertionException("no base url found");
}
builder.withBaseUrl(matcher.group(3));
if (matcher.group(5) == null || matcher.group(5).length() == 0)
{
logger.log(Level.SEVERE, "no project id identified in uri");
throw new AssertionException("no project id found");
}
builder.withProjectId(matcher.group(5));
if (matcher.group(6) == null || matcher.group(6).length() == 0)
{
logger.log(Level.INFO, "no api key identified in uri, creating anonymous instance");
} else
{
builder.withApiKey(matcher.group(7));
}
return builder.build();
}
protected GitlabTicketSystem()
{
logger.log(Level.FINEST,"GitLab Ticketsystem created");
}
/**
* starts builder process for a new gitlab ticket
*/
@Override
public GitlabTicketBuilder createTicket()
{
return new GitlabTicketBuilder(this);
}
/**
* starts filter builder to define constraints and then request those tickets
*/
@Override
public GitlabFilter find()
{
return new GitlabFilter(this);
}
// @Override
// public List<GitlabTicket> getAllTickets()
// {
// logger.log(Level.FINER, "redirecting request to find method");
// return this.find()
// .get();
// }
/**
* This requests a dedicated ticket by it's id.
* It internally calls {@link #find()}, set's the id as filter
* and ensure to get exactly one element as answer.
* @param id ticket id to request
* @throws UnifiedticketingException if more or less than one element in the response
*/
@Override
public GitlabTicket getTicketById(String id)
{
logger.log(Level.FINER, "redirecting request to find method");
List<GitlabTicket> ret = this.find()
.withId(id)
.get();
if (ret == null) return null;
else if (ret.size() != 1) throw new UnifiedticketingException("more or less than 1 dedicated ticket found");
else return ret.get(0);
}
@Override
public boolean hasAssigneeSupport() { return true; }
@Override
public boolean hasDefaultPagination() { return true; }
@Override
public boolean hasLabelSupport() { return true; }
@Override
public boolean hasPaginationSupport() { return true; }
@Override
public boolean hasReturnNullOnErrorSupport() { return true; }
}
package de.hftstuttgart.unifiedticketing.systems.gitlab;
import de.hftstuttgart.unifiedticketing.core.Logging;
import de.hftstuttgart.unifiedticketing.core.TicketSystemBuilder;
import de.hftstuttgart.unifiedticketing.exceptions.AssertionException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GitlabTicketSystemBuilder extends TicketSystemBuilder<GitlabTicketSystemBuilder, GitlabTicketSystem>
{
private static Logger logger = Logging.getLogger(GitlabTicketSystemBuilder.class.getName());
protected String apiKey;
protected String apiVersion;
protected int projectId;
protected boolean https;
/**
* starts builder process for a {@link GitlabTicketSystem} instance
*/
public GitlabTicketSystemBuilder()
{
super();
apiVersion = "v4";
projectId = -1;
https = true;
logger.log(Level.FINEST, "Builder for Gitlab System instance started");
}
/**
* defines api-key to be used for requests<br/>
* <br/>
* if you don't specify an api-key, all requests will be without credentials
* @param apiKey your user specific
* @return this ticket system builder
*/
public GitlabTicketSystemBuilder withApiKey(String apiKey)
{
this.apiKey = apiKey;
logger.log(Level.FINEST, "set api key");
return this;
}
/**
* set protocol to http (no transport security!)
* @return this ticket system builder
*/
public GitlabTicketSystemBuilder withHttp()
{
this.https = false;
logger.log(Level.FINEST, "set http");
return this;
}
/**
* set protocol to https (enabled transport security)<br/>
* this is the default
* @return this ticket system builder
*/
public GitlabTicketSystemBuilder withHttps()
{
this.https = true;
logger.log(Level.FINEST, "set https");
return this;
}
/**
* set project id the instance should work in
* @return this ticket system builder
*/
public GitlabTicketSystemBuilder withProjectId(int projectId)
{
this.projectId = projectId;
logger.log(Level.FINEST, "set project id to " + this.projectId);
return this;
}
/**
* set project id the instance should work in
* @return this ticket system builder
* @throws AssertionException if {@code projectId} is not parsable to as integer
*/
public GitlabTicketSystemBuilder withProjectId(String projectId)
{
try
{
return this.withProjectId(Integer.parseInt(projectId));
}
catch (NumberFormatException e)
{
logger.log(Level.SEVERE, String.format("given String is not parsable to an integer projectId: %s", projectId));
throw new AssertionException(e);
}
}
/**
* builds a new gitlab ticket system instance with the given information.<br/>
* <br/>
* The minimum builder methods needed, are {@link #withBaseUrl} and {@link #withProjectId}.
* @return new {@link GitlabTicketSystem} instance
* @throws AssertionException if {@link #withBaseUrl} or {@link #withProjectId} not called beforehand
*/
@Override
public GitlabTicketSystem build()
{
if (
baseUrl == null
|| projectId == -1
)
{
logger.log(Level.SEVERE, "to build a GitlabTicketSystem instance at least a base url and projectId are needed!");
throw new AssertionException("baseUrl or projectId are missing");
}
GitlabTicketSystem system = new GitlabTicketSystem();
system.baseUrl = String.format(
"%s://%s/api/%s/projects/%d",
(this.https) ? "https" : "http",
this.baseUrl,
this.apiVersion,
this.projectId);
system.apiKey = apiKey;
logger.log(Level.FINEST, "created instance of " + system.getClass().getSimpleName()
+ " with baseUrl " + system.baseUrl);
return system;
}
}
package de.hftstuttgart.unifiedticketing.core;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.*;
public class FilterTest
{
public Filter instance;
@BeforeEach
public void initBeforeEach()
{
instance = mock(Filter.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));
}
@Test
public void testWithSingleAssigneeId()
{
String value = "testId";
Set<String> expected = new HashSet<>();
expected.add(value);
instance.withAssigneeId(value);
assertEquals(expected, instance.setFilters.get(Filter.FilterNames.ASSIGNEEID.name()));
}
@Test
public void testWithMultipleAssigneeId()
{
String[] values = new String[]{"testId", "anotherone", "thirdId"};
Set<String> expected = new HashSet<>(Arrays.asList(values));
Object valueBeforeFirstAdd = instance.setFilters.get(Filter.FilterNames.ASSIGNEEID.name());
instance.withAssigneeId(values[0]);
Object valueAfterFirstAdd = instance.setFilters.get(Filter.FilterNames.ASSIGNEEID.name());
Arrays.stream(values).forEach(instance::withAssigneeId);
Arrays.stream(values).forEach(instance::withAssigneeId);
Object valueAfterAllAdds = instance.setFilters.get(Filter.FilterNames.ASSIGNEEID.name());
assertAll(
() -> assertNull(valueBeforeFirstAdd), // no initial instance present
() -> assertNotNull(valueAfterFirstAdd), // after first add there has to be one
() -> assertSame(valueAfterFirstAdd, valueAfterAllAdds), // check the collection stayed the same
() -> assertEquals(expected, valueAfterAllAdds) // check the content is only in there once and complete
);
}
@Test
public void testWithSingleAssigneeName()
{
String value = "testName";
Set<String> expected = new HashSet<>();
expected.add(value);
instance.withAssigneeName(value);
assertEquals(expected, instance.setFilters.get(Filter.FilterNames.ASSIGNEENAME.name()));
}
@Test
public void testWithMultipleAssigneeName()
{
String[] values = new String[]{"testName", "anotherone", "thirdName"};
Set<String> expected = new HashSet<>(Arrays.asList(values));
Object valueBeforeFirstAdd = instance.setFilters.get(Filter.FilterNames.ASSIGNEENAME.name());
instance.withAssigneeName(values[0]);
Object valueAfterFirstAdd = instance.setFilters.get(Filter.FilterNames.ASSIGNEENAME.name());
Arrays.stream(values).forEach(instance::withAssigneeName);
Arrays.stream(values).forEach(instance::withAssigneeName);
Object valueAfterAllAdds = instance.setFilters.get(Filter.FilterNames.ASSIGNEENAME.name());
assertAll(
() -> assertNull(valueBeforeFirstAdd), // no initial instance present
() -> assertNotNull(valueAfterFirstAdd), // after first add there has to be one
() -> assertSame(valueAfterFirstAdd, valueAfterAllAdds), // check the collection stayed the same
() -> assertEquals(expected, valueAfterAllAdds) // check the content is only in there once and complete
);
}
@Test
public void testWithDescriptionContain()
{
String expected = "test description";
Object valueBeforeFirstAdd = instance.setFilters.get(Filter.FilterNames.DESCRIPTION_CONTAIN.name());
instance.withDescriptionContain(expected);
Object valueAfterAdd = instance.setFilters.get(Filter.FilterNames.DESCRIPTION_CONTAIN.name());
assertAll(
() -> assertNull(valueBeforeFirstAdd), // no initial instance present
() -> assertEquals(expected, valueAfterAdd) // after add there has to be the set one
);
}
@Test
public void testWithDescriptionMatch()
{
String expected = "^some stupid regex number [0-9]+";
Object valueBeforeFirstAdd = instance.setFilters.get(Filter.FilterNames.DESCRIPTION_MATCH.name());
instance.withDescriptionMatch(expected);
Object valueAfterAdd = instance.setFilters.get(Filter.FilterNames.DESCRIPTION_MATCH.name());
assertAll(
() -> assertNull(valueBeforeFirstAdd), // no initial instance present
() -> assertEquals(expected, valueAfterAdd) // after add there has to be the set one
);
}
@Test
public void testWithSingleTicketId()
{
String value = "testId";
Set<String> expected = new HashSet<>();
expected.add(value);
instance.withId(value);
assertEquals(expected, instance.setFilters.get(Filter.FilterNames.IDS.name()));
}
@Test
public void testWithMultipleTicketIds()
{
String[] values = new String[]{"testId", "anotherone", "thirdId"};
Set<String> expected = new HashSet<>(Arrays.asList(values));
Object valueBeforeFirstAdd = instance.setFilters.get(Filter.FilterNames.IDS.name());
instance.withId(values[0]);
Object valueAfterFirstAdd = instance.setFilters.get(Filter.FilterNames.IDS.name());
Arrays.stream(values).forEach(instance::withId);
Arrays.stream(values).forEach(instance::withId);
Object valueAfterAllAdds = instance.setFilters.get(Filter.FilterNames.IDS.name());
assertAll(
() -> assertNull(valueBeforeFirstAdd), // no initial instance present
() -> assertNotNull(valueAfterFirstAdd), // after first add there has to be one
() -> assertSame(valueAfterFirstAdd, valueAfterAllAdds), // check the collection stayed the same
() -> assertEquals(expected, valueAfterAllAdds) // check the content is only in there once and complete
);
}
@Test
public void testIsOpen()
{
instance
.isOpen()
.isClosed()
.isOpen();
assertEquals(Boolean.TRUE, instance.setFilters.get(Filter.FilterNames.OPEN.name()));
}
@Test
public void testIsClosed()
{
instance
.isClosed()
.isOpen()
.isClosed();
assertEquals(Boolean.FALSE, instance.setFilters.get(Filter.FilterNames.OPEN.name()));
}
@Test
public void testWithSingleLabel()
{
String value = "test";
Set<String> expected = new HashSet<>();
expected.add(value);
instance.withLabel(value);
assertEquals(expected, instance.setFilters.get(Filter.FilterNames.LABELS.name()));
}
@Test
public void testWithMultipleLabels()
{
String[] values = new String[]{"test", "To Do", "Bug", "Feature", "review"};
Set<String> expected = new HashSet<>(Arrays.asList(values));
Object valueBeforeFirstAdd = instance.setFilters.get(Filter.FilterNames.LABELS.name());
instance.withLabel(values[0]);
Object valueAfterFirstAdd = instance.setFilters.get(Filter.FilterNames.LABELS.name());
Arrays.stream(values).forEach(instance::withLabel);
Arrays.stream(values).forEach(instance::withLabel);
Object valueAfterAllAdds = instance.setFilters.get(Filter.FilterNames.LABELS.name());
assertAll(
() -> assertNull(valueBeforeFirstAdd), // no initial instance present
() -> assertNotNull(valueAfterFirstAdd), // after first add there has to be one
() -> assertSame(valueAfterFirstAdd, valueAfterAllAdds), // check the collection stayed the same
() -> assertEquals(expected, valueAfterAllAdds) // check the content is only in there once and complete
);
}
@Test
public void testSetPage()
{
int value = 2;
instance.setPage(value);
assertEquals(value, instance.setFilters.get(Filter.FilterNames.PAGE.name()));
}
@Test
public void testSetPageSize()
{
int value = 2;
instance.setPageSize(value);
assertEquals(value, instance.setFilters.get(Filter.FilterNames.PAGINATION.name()));
}
@Test
public void testWithTitleContain()
{
String expected = "test title";
instance.withTitleContain(expected);
assertEquals(expected, instance.setFilters.get(Filter.FilterNames.TITLE_CONTAINS.name()));
}
@Test
public void testWithTitleMatch()
{
String expected = "^some stupid regex number [0-9]+";
instance.withTitleMatch(expected);
assertEquals(expected, instance.setFilters.get(Filter.FilterNames.TITLE_MATCH.name()));
}
@Test
public void testAddToSet()
{
Set<String> expected = new HashSet<>(Arrays.asList("test", "another", "something"));
Map<String, Object> store = new HashMap<>();
expected.forEach(s -> Filter.addToSet(Filter.FilterNames.LABELS, store, s));
assertEquals(expected, store.get(Filter.FilterNames.LABELS.name()));
}
@Test
public void testInitialValues()
{
assertAll(
() -> assertNotNull(instance.setFilters),
() -> assertEquals(0, instance.setFilters.size())
);
}
@Test
public void testFluentApi()
{
Filter before = instance;
Filter after = instance.setPage(1)
.setPageSize(1)
.withAssigneeId("1")
.withAssigneeName("Max")
.withDescriptionContain("Muster")
.withDescriptionMatch("Mann")
.withId("ID")
.withLabel("blabber")
.withTitleContain("hell no")
.withTitleMatch("finally")
.isOpen()
.isClosed();
assertSame(before, after);
}
}
package de.hftstuttgart.unifiedticketing.core;
import de.hftstuttgart.unifiedticketing.systems.gitlab.GitlabTicketSystemBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class RegisteredSystemsTest
{
@Test
public void testGitlab()
{
Class expected = new RegisteredSystems().gitlab().getClass();
Class actual = GitlabTicketSystemBuilder.class;
assertEquals(expected, actual);
}
}
package de.hftstuttgart.unifiedticketing.core;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class TicketAssigneeTest
{
@Test
public void testEquals()
{
TicketAssignee a1 = new TicketAssignee(null, "1", null, null);
TicketAssignee a2 = new TicketAssignee(null, "1", null, null);
TicketAssignee a3 = new TicketAssignee(null, "2", null, null);
assertEquals(a1, a2);
assertNotEquals(a1, a3);
}
}
package de.hftstuttgart.unifiedticketing.core;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class TicketBuilderTest
{
public TicketBuilder instance;
@BeforeEach
public void initBeforeEach()
{
instance = mock(TicketBuilder.class,
withSettings().useConstructor(mock(TicketSystem.class)).defaultAnswer(CALLS_REAL_METHODS));
}
@Test
public void testInit()
{
TicketSystem ts = mock(TicketSystem.class);
instance = mock(TicketBuilder.class, withSettings().useConstructor(ts));
assertAll(
() -> assertSame(ts, instance.parent),
() -> assertNull(instance.description),
() -> assertNull(instance.labels),
() -> assertNull(instance.title)
// seems not possible with mock
// () -> assertThrows(AssertionException.class, () -> new TicketBuilderTestImpl(null))
);
}
@Test
public void testDescription()
{
String expected = "some descriptive stuff";
instance.description(expected);
assertEquals(expected, instance.description);
}
@Test
public void testLabelsBySet()
{
Set<String> expected = new HashSet<>(Arrays.asList("unifiedticketing", "bug", "To Do"));
instance.labels(expected);
assertEquals(expected, instance.labels);
}
@Test
public void testLabelsByArray()
{
String[] input = new String[]{"unifiedticketing", "feature", "review"};
instance.labels(input);
verify(instance, times(1)).labels(new HashSet<>(Arrays.asList(input)));
}
@Test
public void testTitle()
{
String expected = "outstanding title: yolo";
instance.title(expected);
assertEquals(expected, instance.title);
}
@Test
public void testFluentApi()
{
TicketBuilder before = instance;
TicketBuilder after = instance
.description("some")
.labels(new String[]{"random"})
.labels(new HashSet<>(Collections.singleton("values")))
.title("here");
assertSame(before, after);
}
}
package de.hftstuttgart.unifiedticketing.core;
import de.hftstuttgart.unifiedticketing.exceptions.AssertionException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class TicketSystemBuilderTest
{
public TicketSystemBuilder instance;
@BeforeEach
public void initBeforeEach()
{
instance = mock(TicketSystemBuilder.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));
}
@Test
public void testWithBaseUrl()
{
String expected = "some.url.tld";
instance.withBaseUrl(expected);
assertEquals(expected, instance.baseUrl);
assertAll(
() -> assertThrows(AssertionException.class, () -> instance.withBaseUrl("http://urlwithprefix.com")),
() -> assertThrows(AssertionException.class, () -> instance.withBaseUrl("https://urlwithprefix.com"))
);
}
@Test
public void testFluentApi()
{
TicketSystemBuilder before = instance;
TicketSystemBuilder after = instance
.withBaseUrl("teststring");
assertSame(before, after);
}
}
package de.hftstuttgart.unifiedticketing.core;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class TicketSystemTest
{
public TicketSystem instance;
@BeforeEach
public void initBeforeEach()
{
instance = mock(TicketSystem.class,
withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));
}
@Test
public void testInit()
{
assertAll(
() -> assertNull(instance.apiKey),
() -> assertNull(instance.baseUrl),
() -> assertNull(instance.password),
() -> assertNull(instance.username)
);
}
@Test
public void testConfigBoolean()
{
assertFalse(instance.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
instance.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, true);
assertTrue(instance.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
instance.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, false);
assertFalse(instance.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
}
@Test
public void testConfigValue()
{
assertNull(instance.getConfigValue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
String expected = "some string value";
instance.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, expected);
assertFalse(instance.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
assertEquals(expected, instance.getConfigValue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
expected = "TRUE";
instance.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, expected);
assertTrue(instance.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
instance.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, null);
assertFalse(instance.getConfigTrue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
assertEquals(null, instance.getConfigValue(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR));
}
@Test
public void testGetTicketByIdByInt()
{
int input = 5;
instance.getTicketById(input);
verify(instance, times(1)).getTicketById(String.valueOf(input));
}
@Test
public void testFluentApi()
{
TicketSystem before = instance;
TicketSystem after = instance
.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, true)
.config(TicketSystem.ConfigurationOptions.RETURN_NULL_ON_ERROR, "something");
assertSame(before, after);
}
}
package de.hftstuttgart.unifiedticketing.core;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
public class TicketTest
{
private Ticket instance;
@BeforeEach
public void initBeforeEach()
{
instance = mock(Ticket.class,
withSettings().useConstructor(mock(TicketSystem.class)).defaultAnswer(CALLS_REAL_METHODS));
}
@Test
public void testInitialValues()
{
TicketSystem parent = mock(TicketSystem.class);
instance = mock(Ticket.class,
withSettings().useConstructor(parent).defaultAnswer(CALLS_REAL_METHODS));
assertAll(
() -> assertNotNull(instance.assignees),
() -> assertEquals(HashSet.class, instance.assignees.getClass()),
() -> assertNull(instance.description),
() -> assertNull(instance.description),
() -> assertNull(instance.id),
() -> assertNotNull(instance.labels),
() -> assertEquals(HashSet.class, instance.labels.getClass()),
() -> assertTrue(instance.open),
() -> assertNull(instance.title),
() -> assertNotNull(instance.updatedFields),
() -> assertEquals(HashSet.class, instance.updatedFields.getClass()),
() -> assertSame(parent, instance.parent)
// constructor exception check with mock of abstract class not possible I guess?
// () -> assertThrows(AssertionException.class,
// () -> mock(Ticket.class, withSettings().useConstructor((TicketSystem) null)))
);
}
@Test
public void testGetAssignees()
{
assertEquals(instance.assignees, instance.getAssignees());
}
@Test
public void testClearAssignees()
{
Set<TicketAssignee> expected = instance.assignees;
instance.clearAssignees();
assertSame(expected, instance.assignees);
}
@Test
public void testGetDescription()
{
String expected = "some descriptive text";
instance.description = expected;
assertEquals(expected, instance.getDescription());
}
@Test
public void testSetDescription()
{
String expected = "some other descriptive stuff";
instance.setDescription(expected);
assertEquals(expected, instance.description);
}
@Test
public void testGetId()
{
String expected = "T13";
instance.id = expected;
assertEquals(expected, instance.getId());
}
@Test
public void testAddLabel()
{
Set<String> expected = new HashSet<>(Arrays.asList("unifiedticketing", "bug", "feature"));
expected.forEach(instance::addLabel);
assertEquals(expected, instance.labels);
}
@Test
public void testGetLabels()
{
Set<String> expected = new HashSet<>(Arrays.asList("unifiedticketing", "To Do", "enhancement"));
instance.labels = expected;
assertEquals(expected, instance.getLabels());
}
@Test
public void testSetLabelsByList()
{
Set<String> expected = new HashSet<>(Arrays.asList("unifiedticketing", "Doing", "review"));
instance.setLabels(expected);
assertEquals(expected, instance.labels);
}
@Test
public void testSetLabelsByArray()
{
String[] expected = new String[]{"unifiedticketing", "abandoned", "superseded"};
instance.setLabels(expected);
verify(instance, times(1)).setLabels(new HashSet<>(Arrays.asList(expected)));
}
@Test
public void testIsOpen()
{
instance.open = true;
assertTrue(instance.isOpen());
instance.open = false;
assertFalse(instance.isOpen());
}
@Test
public void testOpen()
{
instance.open();
assertTrue(instance.open);
}
@Test
public void testClose()
{
instance.close();
assertFalse(instance.open);
}
@Test
public void testGetParent()
{
assertSame(instance.parent, instance.getParent());
}
@Test
public void testGetTitle()
{
String expected = "fancy title";
instance.title = expected;
assertEquals(expected, instance.getTitle());
}
@Test
public void testSetTitle()
{
String expected = "other title";
instance.setTitle(expected);
assertEquals(expected, instance.title);
}
}