Unverified Commit b841acdd authored by Lukas Wiest's avatar Lukas Wiest 🚂
Browse files

feat: import first state of library code

parent dd0df320
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>unified-ticketing</artifactId>
<version>${buildNumber}</version>
<properties>
<buildNumber>devel</buildNumber>
<java.version>8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<profiles>
<profile>
<id>ci</id>
<activation>
<property><name>env.BUILD_NUMBER</name></property>
</activation>
<properties>
<buildNumber>${env.BUILD_NUMBER}</buildNumber>
</properties>
</profile>
</profiles>
<dependencies>
<!-- &lt;!&ndash; https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.squareup.retrofit2</groupId>-->
<!-- <artifactId>retrofit</artifactId>-->
<!-- <version>2.9.0</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-jackson &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.squareup.retrofit2</groupId>-->
<!-- <artifactId>converter-jackson</artifactId>-->
<!-- <version>2.9.0</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package de.hft.unifiedticketing.core;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <b>Filtered Ticket Request</b><br>
*<br>
* This class provides a builder like interface,
* to get a filtered list of Tickets.<br>
* <br>
* It provides some basic filter options by itself.
* The request logic has to be provided from each System specific
* implementation for it.
*
* @param <T> The type of Ticket this Filter implementation delivers
* @param <F> The type of the System specific implementation of this class
*/
public abstract class Filter<T extends Ticket, F extends Filter<?,?>>
{
private static Logger logger = Logging.getLogger(Filter.class.getName());
protected Map<String, Object> setFilters;// = new HashMap<>();
protected enum FilterNames
{
ASSIGNEEID,
ASSIGNEENAME,
DESCRIPTION_CONTAIN,
DESCRIPTION_MATCH,
IDS,
LABELS,
OPEN,
PAGE,
PAGINATION,
TITLE_CONTAINS,
TITLE_MATCH,
}
public Filter()
{
logger.log(Level.FINEST, String.format(
"%s request builder started",
this.getClass().getSimpleName()
));
setFilters = new HashMap<>();
}
/**
* Add assignee id to query
* @return this query builder
*/
public F withAssigneeId(String id)
{
addToSet(FilterNames.ASSIGNEEID, this.setFilters, id);
return (F) this;
}
/**
* Add assignee name to query
* @return this query builder
*/
public F withAssigneeName(String name)
{
addToSet(FilterNames.ASSIGNEENAME, this.setFilters, name);
return (F) this;
}
/**
* adds description contain constraint
* @param substring element to be contained in the description
* @return this query builder
*/
public F withDescriptionContain(String substring)
{
setFilters.put(FilterNames.DESCRIPTION_CONTAIN.name(), substring);
logger.log(Level.FINEST, String.format("added constraint: %s %s", FilterNames.DESCRIPTION_CONTAIN.name(), substring));
return (F) this;
}
/**
* regex that the whole description must match
* @param regex fully qualified regex
* @return this query builder
*/
public F withDescriptionMatch(String regex)
{
setFilters.put(FilterNames.DESCRIPTION_MATCH.name(), regex);
logger.log(Level.FINEST, String.format("added constraint: %s %s", FilterNames.DESCRIPTION_MATCH.name(), regex));
return (F) this;
}
/**
* adds an ticket id to the constraint list
* @param id
* @return this query builder
*/
public F withId(String id)
{
addToSet(FilterNames.IDS, this.setFilters, id);
return (F) this;
}
/**
* adds a constraint for only open considered tickets
* @return this query builder
*/
public F isOpen()
{
setFilters.put(FilterNames.OPEN.name(), true);
logger.log(Level.FINEST, String.format("added constraint: %s %s", FilterNames.OPEN.name(), true));
return (F) this;
}
/**
* adds a constraint for only closed considered tickets
* @return this query builder
*/
public F isClosed()
{
setFilters.put(FilterNames.OPEN.name(), false);
logger.log(Level.FINEST, String.format("added constraint: %s %s", FilterNames.OPEN.name(), false));
return (F) this;
}
/**
* add a label to the constraints list<br>
*<br>
* multiple calls of this delivers only tickets holding ALL given labels.
* @param label
* @return this query builder
*/
public F withLabel(String label)
{
addToSet(FilterNames.LABELS, this.setFilters, label);
return (F) this;
}
/**
* set the page number of the pagination<br>
*<br>
* <b>Attention:</b> some systems paginate by default.
* You can check that with {@link TicketSystem}s "has..." methods.
* @param page
* @return this query builder
*/
public F setPage(int page)
{
this.setFilters.put(FilterNames.PAGE.name(), page);
logger.log(Level.FINEST, String.format("set page: %d", page));
return (F) this;
}
/**
* defines the page size of a single pagination page<br>
*<br>
* <b>Attention:</b> some systems have a default in place, if nothing custom is set.
* You can check that with {@link TicketSystem}s "has..." methods.
* @param size
* @return this query builder
*/
public F setPageSize(int size)
{
this.setFilters.put(FilterNames.PAGINATION.name(), size);
logger.log(Level.FINEST, String.format("set pagination size: %d", size));
return (F) this;
}
/**
* adds a constraint for title containing the given string
* @param substring string the title must contain
* @return this query builder
*/
public F withTitleContain(String substring)
{
setFilters.put(FilterNames.TITLE_CONTAINS.name(), substring);
logger.log(Level.FINEST, String.format("added constraint: %s %s", FilterNames.TITLE_CONTAINS.name(), substring));
return (F) this;
}
/**
* adds a regex constraint, that the title has to match
* @param regex fully qualified regex
* @return this query builder
*/
public F withTitleMatch(String regex)
{
setFilters.put(FilterNames.TITLE_MATCH.name(), regex);
logger.log(Level.FINEST, String.format("added constraint: %s %s", FilterNames.TITLE_MATCH.name(), regex));
return (F) this;
}
/**
* <b>Requesting the tickets</b><br>
*<br>
* This will prepare the request according to the system specific implementation,
* run the request, if needed post-filter the results and form it into a List
* @return List of Tickets, matching all previously set constraints
*/
public abstract List<T> get();
protected static void addToSet(FilterNames filterName, Map<String, Object> map, String newValue)
{
logger.log(Level.FINEST, String.format("attempting to add new %s constraint", filterName.name()));
Set<String> values;
if (map.containsKey(filterName.name()))
{
logger.log(Level.FINEST, String.format("existing %s constraints found", filterName.name()));
Object stored = map.get(filterName.name());
if (stored instanceof Set<?>)
{
values = (Set<String>) stored;
} else
{
logger.log(Level.WARNING, String.format(
"found filter object under key %s was not from Type %s but %s",
filterName.name(),
Set.class.getName(),
stored.getClass().getName()));
values = new HashSet<>();
map.put(filterName.name(), values);
logger.log(Level.INFO, String.format(
"replaced wrong typed filter object under key %s with new instance of type %s",
filterName.name(),
Set.class.getName()));
}
} else
{
logger.log(Level.FINEST, String.format("no previous %s constraints found", filterName));
values = new HashSet<>();
map.put(filterName.name(), values);
}
values.add(newValue);
logger.log(Level.FINEST, String.format("added constraint: %s %s", filterName.name(), newValue));
}
}
package de.hft.unifiedticketing.core;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.*;
public class Logging
{
private static Handler handler;
private static Logger mainLogger;
private static Logger logger;
static
{
mainLogger = Logger.getLogger("de.hft.unifiedticketing");
mainLogger.setUseParentHandlers(false);
handler = new ConsoleHandler();
handler.setFormatter(new SimpleFormatter() {
private static final String format = "[%1$tFT%1$tT%1$tz] [%2$-7s] [%3$s.%4$s] %5$s%n";
@Override
public synchronized String format(LogRecord lr) {
return String.format(format,
new Date(lr.getMillis()),
lr.getLevel(),
lr.getSourceClassName(),
lr.getSourceMethodName(),
lr.getMessage()
);
}
});
handler.setLevel(Level.INFO);
mainLogger.addHandler(handler);
logger = getLogger(Logging.class.getName());
}
public static Logger getLogger(String name)
{
return Logger.getLogger(name);
}
public static void setFormatter(Formatter formatter)
{
handler.setFormatter(formatter);
}
public static void setLevel(Level level)
{
handler.setLevel(level);
mainLogger.setLevel(level);
}
public static void test(String message)
{
logger.log(Level.FINEST, message);
logger.log(Level.FINER, message);
logger.log(Level.FINE, message);
logger.log(Level.INFO, message);
logger.log(Level.WARNING, message);
logger.log(Level.SEVERE, message);
}
}
package de.hft.unifiedticketing.core;
import de.hft.unifiedticketing.systems.gitlab.GitlabTicketSystemBuilder;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class is where for each supported system a method is placed, that returns a new Builder instance for it.
*/
public class RegisteredSystems
{
private static Logger logger = Logging.getLogger(RegisteredSystems.class.getName());
private static RegisteredSystems instance = null;
protected RegisteredSystems() { }
protected static RegisteredSystems getInstance()
{
if (instance == null)
{
instance = new RegisteredSystems();
logger.log(Level.FINEST, "Singleton instance created");
}
return instance;
}
/**
* Starts the builder mechanism for a GitLab connection
*/
public GitlabTicketSystemBuilder gitlab()
{
return new GitlabTicketSystemBuilder();
}
}
package de.hft.unifiedticketing.core;
import de.hft.unifiedticketing.exceptions.AssertionException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <b>Ticket representation</b><br/>
* <br/>
* This class is the generic representation of a ticket for the supported systems.
* It defines a few fields as well as getters and setters for them.<br/>
* <br/>
* Next there's a save method, to persist your changes back into the real ticket system.
* This is is a method implemented in each of the system specific child classes.
*
* @param <TS> Ticketsystem a ticket instance belongs to
* @param <T> child implementation of this class an instance belongs to
*/
public abstract class Ticket<TS extends TicketSystem, T extends Ticket>
{
private static Logger logger = Logging.getLogger(Ticket.class.getName());
protected Set<TicketAssignee> assignees;
protected String description;
protected String id;
protected Set<String> labels;
protected boolean open;
protected String title;
protected Set<String> updatedFields;
protected TS parent;
protected enum FieldNames
{
ASSIGNEES,
DESCRIPTION,
ID,
LABELS,
OPEN,
TITLE,
}
/**
* @param parent Ticketsystem instance, this ticket is from
*/
protected Ticket(TS parent)
{
if (parent == null)
{
logger.log(Level.SEVERE, "every ticket instance needs a belonging ticket system, but received null!");
throw new AssertionException(
String.format("Got null as parent ticket system on creating new %s", this.getClass().getSimpleName()));
}
this.assignees = new HashSet<>();
this.labels = new HashSet<>();
this.open = true;
this.parent = parent;
this.updatedFields = new HashSet<>();
}
public Set<? extends TicketAssignee> getAssignees()
{
if (this.assignees == null) return new HashSet<>(); // TODO remove as assignees should NEVER turn null
else return this.assignees;
}
/**
* adds an new assignee to the ticket, by the systems api primary identifier for assignees
* @return this ticket
*/
public abstract T addAssignee(String identifier);
/**
* clears all assignees
* @return this ticket
*/
public T clearAssignees()
{
this.assignees.clear();
this.updatedFields.add(FieldNames.ASSIGNEES.name());
return (T) this;
}
/**
* removes a single assignee identified by the platforms api primary identifier for assignees
* @return this ticket
*/
public abstract T removeAssignee(String identifier);
public String getDescription()
{
return description;
}
/**
* replaces the current ticket description with the new given one
* @return this ticket
*/
public T setDescription(String description)
{
this.description = description;
this.updatedFields.add(FieldNames.DESCRIPTION.name());
logger.log(Level.FINEST, String.format(
"[Ticket: %s] %s marked for update",
this.id,
FieldNames.DESCRIPTION.name()
));
return (T) this;
}
public String getId()
{
return id;
}
/**
* Adds the given label, preserving the yet assigned ones
* @return this ticket
*/
public T addLabel(String label)
{
this.labels.add(label);
this.updatedFields.add(FieldNames.LABELS.name());
logger.log(Level.FINEST, String.format(
"[Ticket: %s] %s marked for update",
this.id,
FieldNames.LABELS.name()
));
return (T) this;
}
public Set<String> getLabels()
{
return labels;
}
/**
* replaces all labels of the ticket with the new ones
* @param labels collection to replace old ones
* @return this ticket
*/
public T setLabels(Set<String> labels)
{
this.labels = labels;
this.updatedFields.add(FieldNames.LABELS.name());
logger.log(Level.FINEST, String.format(
"[Ticket: %s] %s marked for update",
this.id,
FieldNames.LABELS.name()
));
return (T) this;
}
/**
* replaces all labels of the ticket with the new ones
* @param labels array to replace old ones
* @return this ticket
*/
public T setLabels(String[] labels)
{
logger.log(Level.FINEST, String.format(
"[Ticket %s] transforming labels Array to Set",
this.id
));
return this.setLabels(new HashSet<>(Arrays.asList(labels)));
}
public boolean isOpen()
{
return open;
}
/**
* set's the ticket state to a open considered state
* @return this ticket
*/
public T open()
{
this.updatedFields.add(FieldNames.OPEN.name());
this.open = true;
logger.log(Level.FINEST, String.format(
"[Ticket: %s] %s marked for update",
this.id,
FieldNames.OPEN.name()
));
return (T) this;
}
/**
* set's the ticket state to a closed considered state
* @return this ticket
*/
public T close()
{
this.updatedFields.add(FieldNames.OPEN.name());
this.open = false;
logger.log(Level.FINEST, String.format(
"[Ticket: %s] %s marked for update",
this.id,
FieldNames.OPEN.name()
));
return (T) this;
}
public TS getParent()
{
return parent;
}
public String getTitle()
{
return title;
}
/**
* replaces old title
* @return this ticket
*/
public T setTitle(String title)
{
this.updatedFields.add(FieldNames.TITLE.name());
this.title = title;
logger.log(Level.FINEST, String.format(
"[Ticket: %s] %s marked for update",
this.id,
FieldNames.TITLE.name()
));
return (T) this;
}
/**
* persist all fields marked as tampered into a request to update them in the real ticket system
* @return the changed ticket requested from the real ticket system after updating
*/
public abstract T save();
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || !Objects.equals(o.getClass(), this.getClass())) return false;
Ticket<?, ?> ticket = (Ticket<?, ?>) o;
return Objects.equals(id, ticket.id) &&
parent == ticket.parent;
}
@Override
public int hashCode()
{
return Objects.hash(id, parent);
}
@Override
public String toString()
{
return "Ticket{" +
"assignees: " + assignees +
", description='" + description + '\'' +
", id='" + id + '\'' +
", labels=" + labels +
", open=" + open +
", title='" + title + '\'' +
'}';
}
}
package de.hft.unifiedticketing.core;
import java.util.Objects;
public class TicketAssignee
{
public final String email;
public final String id;
public final String fullName;
public final String username;
protected TicketAssignee(String email, String id, String username, String fullName)
{
this.email = email;
this.id = id;
this.fullName = fullName;
this.username = username;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof TicketAssignee)) return false;
TicketAssignee that = (TicketAssignee) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode()
{
return Objects.hash(id);
}
@Override
public String toString()
{
return "TicketAssignee{" +
"email='" + email + '\'' +
", id='" + id + '\'' +
", fullName='" + fullName + '\'' +
", username='" + username + '\'' +
'}';
}
}
package de.hft.unifiedticketing.core;
import de.hft.unifiedticketing.exceptions.AssertionException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* <b>Ticket Builder</b><br/>
* <br/>
* This class provides a builder mechanism, to define a new Ticket and create it afterwards.
* @param <B> implementation of this class this instance belongs to
* @param <T> type of ticket implementation this builder will create
* @param <TS> type of ticket system this builder belongs to
*/
public abstract class TicketBuilder<B extends TicketBuilder, T extends Ticket, TS extends TicketSystem>
{
private static Logger logger = Logging.getLogger(TicketBuilder.class.getName());
public final TS parent;
protected Set<Integer> assignees;
protected String description;
protected Set<String> labels;
protected String title;
protected TicketBuilder(TS parent)
{
if (parent == null)
{
logger.log(Level.SEVERE, "every ticket builder instance needs a belonging ticket system, but received null!");
throw new AssertionException(
String.format("Got null as parent ticket system on creating new %s", this.getClass().getSimpleName()));
}
this.parent = parent;
logger.log(Level.FINEST, String.format("%s instantiated", this.getClass().getSimpleName()));
}
public abstract B assignees(String... identifiers);
public B description(String description)
{
this.description = description;
logger.log(Level.FINEST, "description added to new Ticket");
return (B) this;
}
public B labels(Set<String> labels)
{
this.labels = labels;
logger.log(Level.FINEST, "labels added to new Ticket");
return (B) this;
}
public B labels(String[] labels)
{
logger.log(Level.FINEST, "transforming labels Array to Set");
return this.labels(new HashSet<>(Arrays.asList(labels)));
}
public B title(String title)
{
this.title = title;
logger.log(Level.FINEST, "title added to new Ticket");
return (B) this;
}
/**
* persists the defined ticket into a create request to the ticket system connected
* @return newly created ticket
*/
public abstract T create();
}
package de.hft.unifiedticketing.core;
import de.hft.unifiedticketing.exceptions.AssertionException;
import de.hft.unifiedticketing.systems.gitlab.GitlabTicketSystem;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Defines the representation of a ticket system connection<br/>
* <br/>
*
* @param <T> ticket implementation this ticket system uses
* @param <TS> implementation of this class an instance belongs to
* @param <TB> ticket builder this ticket system uses
* @param <F> filter implementation this ticket system uses
*/
public abstract class TicketSystem<T extends Ticket, TS extends TicketSystem, TB extends TicketBuilder, F extends Filter>
{
private static Logger logger = Logging.getLogger(TicketSystem.class.getName());
protected HashMap<String, String> configuration = new HashMap<>();
/**
* Global configuration options for ticket system instances, settable with the method config
*/
public enum ConfigurationOptions
{
RETURN_NULL_ON_ERROR
}
protected TicketSystem() { }
/**
* creates a ticketsystem from an uri<br/>
* <br/>
* a valid uri for this library always starts with "unifiedticketing" followed by a colon.
* Next is the identifier for the desired ticket system.
* The available systems and what identifier they need, can be found in the documentation.
* <br/>
* example: {@code "unifiedticketing:<systemidentifier>:<system specific things>"}
*
* @return ticket system instance
*/
public static TicketSystem fromUri(String uri)
{
Pattern pattern = Pattern.compile("^(unifiedticketing):([a-zA-Z0-9_]+):(.*)$");
Matcher matcher = pattern.matcher(uri);
if (!matcher.matches())
{
logger.log(Level.SEVERE, "given URI not for unifiedticketing!");
throw new AssertionException(String.format("URI %s not for unifiedticketing", uri));
}
switch (matcher.group(2))
{
case "gitlab":
return GitlabTicketSystem.fromUri(matcher.group(3));
default:
logger.log(Level.SEVERE, String.format(
"Unknown system identifier: %s",
matcher.group(2)
));
throw new AssertionException(String.format(
"no supported system implementation found for %s",
matcher.group(2)
));
}
}
/**
* @return builder selection
*/
public static RegisteredSystems fromBuilder()
{
return RegisteredSystems.getInstance();
}
// Instance definition =====================================================================//
public String apiKey;
public String baseUrl;
public String password;
public String username;
/**
* sets a value configuration
* @param option configuration to set
* @param value value to be stored
* @return this ticket system
*/
public TS config(ConfigurationOptions option, String value)
{
this.configuration.put(option.name(), value);
return (TS) this;
}
/**
* enables/disables a configuration
* @param option configuration to set
* @param value true to enable the setting
* @return this ticket system
*/
public TS config(ConfigurationOptions option, boolean value)
{
return config(option, String.valueOf(value));
}
/**
* @param option configuration to check
* @return true if configuration has stored "true"
*/
public boolean getConfigTrue(ConfigurationOptions option)
{
return Boolean.parseBoolean(this.configuration.get(option.name()));
}
/**
* @param option configuration to get stored value
* @return stored value of given config
*/
public Object getConfigValue(ConfigurationOptions option)
{
return this.configuration.get(option.name());
}
/**
* starts ticket builder for this ticket system
*/
public abstract TB createTicket();
/**
* starts ticket filter for this ticket system
*/
public abstract F find();
// public abstract List<T> getAllTickets();
/**
* requests a single ticket by it's id
*/
public abstract T getTicketById(String id);
public T getTicketById(int id)
{
logger.log(Level.FINEST, "transforming id to String");
return this.getTicketById(String.valueOf(id));
}
public abstract boolean hasAssigneeSupport();
public abstract boolean hasDefaultPagination();
public abstract boolean hasLabelSupport();
public abstract boolean hasPaginationSupport();
public abstract boolean hasReturnNullOnErrorSupport();
// public static boolean hasUriBuildSupport();
}
package de.hft.unifiedticketing.core;
import de.hft.unifiedticketing.exceptions.AssertionException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Builder class for ticket systems
* @param <B> type of builder implementation
* @param <TS> type of ticket system this builder creates
*/
public abstract class TicketSystemBuilder<B extends TicketSystemBuilder, TS extends TicketSystem>
{
private static Logger logger = Logging.getLogger(TicketSystemBuilder.class.getName());
public String baseUrl;
/**
* set's the base url of a system
* @param url base url without protocol prefix
* @return this builder
*/
public B withBaseUrl(String url)
{
if (url.startsWith("http://") || url.startsWith("https://"))
{
logger.log(Level.SEVERE, "base url not allowed to start with protocol!");
throw new AssertionException(String.format("url %s started with protocol which is not allowed", url));
}
baseUrl = url;
logger.log(Level.FINEST, "set base url to " + baseUrl);
return (B) this;
}
/**
* creates a new ticket system instance from the previously set information
* @return new ticket system instance
*/
public abstract TS build();
}
package de.hft.unifiedticketing.exceptions;
public class AssertionException extends UnifiedticketingException
{
public AssertionException() { super(); }
public AssertionException(String msg)
{
super(msg);
}
public AssertionException(String msg, Throwable cause)
{
super(msg, cause);
}
public AssertionException(Throwable suppressed) { super(suppressed); }
}
package de.hft.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.hft.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.hft.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.hft.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.hft.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.hft.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.hft.unifiedticketing.systems.gitlab;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.hft.unifiedticketing.core.Filter;
import de.hft.unifiedticketing.core.Logging;
import de.hft.unifiedticketing.core.TicketSystem;
import de.hft.unifiedticketing.exceptions.AssertionException;
import de.hft.unifiedticketing.exceptions.DeserializationException;
import de.hft.unifiedticketing.exceptions.HttpReqeustException;
import de.hft.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.hft.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.hft.unifiedticketing.core.Logging;
import de.hft.unifiedticketing.core.Ticket;
import de.hft.unifiedticketing.core.TicketSystem;
import de.hft.unifiedticketing.exceptions.*;
import okhttp3.*;
import okio.Buffer;
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.hft.unifiedticketing.systems.gitlab;
import de.hft.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.hft.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.hft.unifiedticketing.core.Logging;
import de.hft.unifiedticketing.core.TicketBuilder;
import de.hft.unifiedticketing.core.TicketSystem;
import de.hft.unifiedticketing.exceptions.*;
import okhttp3.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
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
{
assignees(Arrays.stream(identifiers)
.mapToInt(Integer::parseInt)
.toArray());
} catch (NumberFormatException e)
{
logger.log(Level.SEVERE, String.format("not as integer parsable assignee id encountered!"));
throw new AssertionException(e);
}
return this;
}
public GitlabTicketBuilder assignees(int... identifiers)
{
assignees = Arrays.stream(identifiers)
.mapToObj(i -> (Integer) i)
.collect(Collectors.toSet());
return this;
}
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");
}
else body.put("title", this.title);
logger.log(Level.FINEST, "title set");
if (this.assignees != null)
{
ArrayNode ids = body.putArray("assignee_ids");
assignees.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);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment