diff --git a/src/main/java/pl/itcrowd/youtrack/api/ErrorUnmarshaller.java b/src/main/java/pl/itcrowd/youtrack/api/ErrorUnmarshaller.java deleted file mode 100644 index 7295e5d..0000000 --- a/src/main/java/pl/itcrowd/youtrack/api/ErrorUnmarshaller.java +++ /dev/null @@ -1,39 +0,0 @@ -package pl.itcrowd.youtrack.api; - -import org.apache.commons.io.IOUtils; -import pl.itcrowd.youtrack.api.rest.ObjectFactory; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -//TODO methods from this class should be probably merged with YoutrackUnmarshaller -public final class ErrorUnmarshaller { - - public static String unmarshal(String string) throws JAXBException, IOException - { - return unmarshal(new StringReader(string)); - } - - public static String unmarshal(Reader reader) throws JAXBException, IOException - { - String content = IOUtils.toString(reader); - try { - JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class); - @SuppressWarnings("unchecked") - final JAXBElement element = (JAXBElement) jaxbContext.createUnmarshaller().unmarshal(new StringReader(content)); - return element.getValue(); - } catch (JAXBException e) { -// TODO we need logging here - System.err.println("Cannot unmarshal input stream.\n" + content + e); - throw e; - } - } - - private ErrorUnmarshaller() - { - } -} diff --git a/src/main/java/pl/itcrowd/youtrack/api/YoutrackAPI.java b/src/main/java/pl/itcrowd/youtrack/api/YoutrackAPI.java index f92ea74..26d7786 100644 --- a/src/main/java/pl/itcrowd/youtrack/api/YoutrackAPI.java +++ b/src/main/java/pl/itcrowd/youtrack/api/YoutrackAPI.java @@ -345,13 +345,14 @@ public class YoutrackAPI { private String execute(HttpUriRequest request) throws IOException { + request.addHeader("Accept","application/xml"); final HttpResponse response = httpClient.execute(request); final StatusLine statusLine = response.getStatusLine(); final HttpEntity entity = response.getEntity(); String responseText = entity == null ? null : EntityUtils.toString(entity); if (statusLine.getStatusCode() >= 300) { try { - final String error = ErrorUnmarshaller.unmarshal(responseText); + final String error = YoutrackUnmarshaller.unmarshalError(responseText); throw new YoutrackErrorException(error, statusLine.getStatusCode()); } catch (JAXBException e) { LOG.error("Cannot unmarshal following response text:\n" + responseText, e); diff --git a/src/main/java/pl/itcrowd/youtrack/api/YoutrackUnmarshaller.java b/src/main/java/pl/itcrowd/youtrack/api/YoutrackUnmarshaller.java index 4c9688c..0075127 100644 --- a/src/main/java/pl/itcrowd/youtrack/api/YoutrackUnmarshaller.java +++ b/src/main/java/pl/itcrowd/youtrack/api/YoutrackUnmarshaller.java @@ -1,14 +1,71 @@ package pl.itcrowd.youtrack.api; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import pl.itcrowd.youtrack.api.exceptions.YoutrackAPIException; +import pl.itcrowd.youtrack.api.rest.ErrorType; import pl.itcrowd.youtrack.api.rest.ObjectFactory; import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; +import java.io.IOException; import java.io.Reader; +import java.io.Serializable; import java.io.StringReader; public final class YoutrackUnmarshaller { + private static Log LOG = LogFactory.getLog(YoutrackUnmarshaller.class); + + private YoutrackUnmarshaller() + { + } + + public static String unmarshalError(String string) throws JAXBException, IOException + { + return unmarshalError(new StringReader(string)); + } + + public static String unmarshalError(Reader reader) throws JAXBException, IOException + { + String content = IOUtils.toString(reader); + try { + JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class); + @SuppressWarnings("unchecked") + final JAXBElement element = (JAXBElement) jaxbContext.createUnmarshaller().unmarshal(new StringReader(content)); + final ErrorType value = element.getValue(); + final int contentSize = value.getContent().size(); + if (contentSize == 0) { + return ""; + } else if (contentSize == 1) { + final Serializable serializable = value.getContent().get(0); + if (serializable instanceof JAXBElement) { + if ("message".equals(((JAXBElement) serializable).getName().getLocalPart())) { + final Object messageValue = ((JAXBElement) serializable).getValue(); + return messageValue == null ? "" : messageValue.toString(); + } + } else { + return serializable == null ? "" : serializable.toString(); + } + } else if (contentSize == 2) { + for (Serializable serializable : value.getContent()) { + if (serializable instanceof JAXBElement) { + if ("message".equals(((JAXBElement) serializable).getName().getLocalPart())) { + final Object messageValue = ((JAXBElement) serializable).getValue(); + return messageValue == null ? "" : messageValue.toString(); + } + } + } + } + throw new YoutrackAPIException("Cannot unserialize error.\n" + content); + } catch (JAXBException e) { + LOG.error("Cannot unmarshal error.\n" + content, e); + throw e; + } + } + public static Object unmarshall(String string) throws JAXBException { return unmarshall(new StringReader(string)); @@ -18,8 +75,4 @@ public final class YoutrackUnmarshaller { { return JAXBContext.newInstance(ObjectFactory.class).createUnmarshaller().unmarshal(reader); } - - private YoutrackUnmarshaller() - { - } } diff --git a/src/main/java/pl/itcrowd/youtrack/api/rest/Comment.java b/src/main/java/pl/itcrowd/youtrack/api/rest/Comment.java index 334aaa2..e2170c2 100644 --- a/src/main/java/pl/itcrowd/youtrack/api/rest/Comment.java +++ b/src/main/java/pl/itcrowd/youtrack/api/rest/Comment.java @@ -108,8 +108,8 @@ public class Comment { *

* Objects of the following type(s) are allowed in the list * {@link JAXBElement }{@code <}{@link Comment.Value }{@code >} - * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link String } + * {@link JAXBElement }{@code <}{@link String }{@code >} */ public List getContent() { diff --git a/src/main/java/pl/itcrowd/youtrack/api/rest/ErrorType.java b/src/main/java/pl/itcrowd/youtrack/api/rest/ErrorType.java new file mode 100644 index 0000000..8b5b901 --- /dev/null +++ b/src/main/java/pl/itcrowd/youtrack/api/rest/ErrorType.java @@ -0,0 +1,74 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-833 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// +package pl.itcrowd.youtrack.api.rest; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlElementRefs; +import javax.xml.bind.annotation.XmlMixed; +import javax.xml.bind.annotation.XmlType; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + *

Java class for errorType complex type. + *

+ *

The following schema fragment specifies the expected content contained within this class. + *

+ *

+ * <complexType name="errorType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="message" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ *         <element name="field" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "errorType", propOrder = {"content"}) +public class ErrorType { + + @XmlElementRefs({@XmlElementRef(name = "message", type = JAXBElement.class), @XmlElementRef(name = "field", type = JAXBElement.class)}) + @XmlMixed + protected List content; + + /** + * Gets the value of the content property. + *

+ *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the content property. + *

+ *

+ * For example, to add a new item, do as follows: + *

+     *    getContent().add(newItem);
+     * 
+ *

+ *

+ *

+ * Objects of the following type(s) are allowed in the list + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link String } + * {@link JAXBElement }{@code <}{@link String }{@code >} + */ + public List getContent() + { + if (content == null) { + content = new ArrayList(); + } + return this.content; + } +} diff --git a/src/main/java/pl/itcrowd/youtrack/api/rest/Issue.java b/src/main/java/pl/itcrowd/youtrack/api/rest/Issue.java index 7fe706f..5a9c9ee 100644 --- a/src/main/java/pl/itcrowd/youtrack/api/rest/Issue.java +++ b/src/main/java/pl/itcrowd/youtrack/api/rest/Issue.java @@ -37,7 +37,7 @@ import java.util.List; @XmlType(name = "issueType", propOrder = {"fieldOrComment"}) public class Issue { - @XmlElements({@XmlElement(name = "comment", type = Comment.class), @XmlElement(name = "field", type = Field.class)}) + @XmlElements({@XmlElement(name = "field", type = Field.class), @XmlElement(name = "comment", type = Comment.class)}) protected List fieldOrComment; @XmlAttribute @@ -61,8 +61,8 @@ public class Issue { *

*

* Objects of the following type(s) are allowed in the list - * {@link Comment } * {@link Field } + * {@link Comment } */ public List getFieldOrComment() { diff --git a/src/main/java/pl/itcrowd/youtrack/api/rest/ObjectFactory.java b/src/main/java/pl/itcrowd/youtrack/api/rest/ObjectFactory.java index 96dc2e6..75efb9e 100644 --- a/src/main/java/pl/itcrowd/youtrack/api/rest/ObjectFactory.java +++ b/src/main/java/pl/itcrowd/youtrack/api/rest/ObjectFactory.java @@ -38,6 +38,10 @@ public class ObjectFactory { private final static QName _CommentValue_QNAME = new QName("", "value"); + private final static QName _ErrorTypeField_QNAME = new QName("", "field"); + + private final static QName _ErrorTypeMessage_QNAME = new QName("", "message"); + /** * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: pl.itcrowd.youtrack.api.rest */ @@ -46,27 +50,27 @@ public class ObjectFactory { } /** - * Create an instance of {@link Comment.Value } + * Create an instance of {@link Field } */ - public Comment.Value createCommentValue() + public Field createField() { - return new Comment.Value(); + return new Field(); } /** - * Create an instance of {@link EnumerationValue } + * Create an instance of {@link Enumeration } */ - public EnumerationValue createEnumerationValue() + public Enumeration createEnumeration() { - return new EnumerationValue(); + return new Enumeration(); } /** - * Create an instance of {@link Issue } + * Create an instance of {@link User } */ - public Issue createIssue() + public User createUser() { - return new Issue(); + return new User(); } /** @@ -78,59 +82,67 @@ public class ObjectFactory { } /** - * Create an instance of {@link User } + * Create an instance of {@link Issues } */ - public User createUser() + public Issues createIssues() { - return new User(); + return new Issues(); } /** - * Create an instance of {@link Issues } + * Create an instance of {@link AssigneeList } */ - public Issues createIssues() + public AssigneeList createAssigneeList() { - return new Issues(); + return new AssigneeList(); } /** - * Create an instance of {@link Field } + * Create an instance of {@link Field.Value } */ - public Field createField() + public Field.Value createFieldValue() { - return new Field(); + return new Field.Value(); } /** - * Create an instance of {@link Comment } + * Create an instance of {@link AssigneeType } */ - public Comment createComment() + public AssigneeType createAssigneeType() { - return new Comment(); + return new AssigneeType(); } /** - * Create an instance of {@link AssigneeList.Assignees } + * Create an instance of {@link ErrorType } */ - public AssigneeList.Assignees createAssigneeListAssignees() + public ErrorType createErrorType() { - return new AssigneeList.Assignees(); + return new ErrorType(); } /** - * Create an instance of {@link Enumeration } + * Create an instance of {@link AssignedByType } */ - public Enumeration createEnumeration() + public AssignedByType createAssignedByType() { - return new Enumeration(); + return new AssignedByType(); } /** - * Create an instance of {@link AssigneeList } + * Create an instance of {@link EnumerationValue } */ - public AssigneeList createAssigneeList() + public EnumerationValue createEnumerationValue() { - return new AssigneeList(); + return new EnumerationValue(); + } + + /** + * Create an instance of {@link AssigneeList.Assignees } + */ + public AssigneeList.Assignees createAssigneeListAssignees() + { + return new AssigneeList.Assignees(); } /** @@ -142,36 +154,36 @@ public class ObjectFactory { } /** - * Create an instance of {@link Field.Value } + * Create an instance of {@link Comment } */ - public Field.Value createFieldValue() + public Comment createComment() { - return new Field.Value(); + return new Comment(); } /** - * Create an instance of {@link AssignedByType } + * Create an instance of {@link Comment.Value } */ - public AssignedByType createAssignedByType() + public Comment.Value createCommentValue() { - return new AssignedByType(); + return new Comment.Value(); } /** - * Create an instance of {@link AssigneeType } + * Create an instance of {@link Issue } */ - public AssigneeType createAssigneeType() + public Issue createIssue() { - return new AssigneeType(); + return new Issue(); } /** - * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}} + * Create an instance of {@link JAXBElement }{@code <}{@link ErrorType }{@code >}} */ @XmlElementDecl(namespace = "", name = "error") - public JAXBElement createError(String value) + public JAXBElement createError(ErrorType value) { - return new JAXBElement(_Error_QNAME, String.class, null, value); + return new JAXBElement(_Error_QNAME, ErrorType.class, null, value); } /** @@ -218,4 +230,22 @@ public class ObjectFactory { { return new JAXBElement(_CommentValue_QNAME, Comment.Value.class, Comment.class, value); } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}} + */ + @XmlElementDecl(namespace = "", name = "field", scope = ErrorType.class) + public JAXBElement createErrorTypeField(String value) + { + return new JAXBElement(_ErrorTypeField_QNAME, String.class, ErrorType.class, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}} + */ + @XmlElementDecl(namespace = "", name = "message", scope = ErrorType.class) + public JAXBElement createErrorTypeMessage(String value) + { + return new JAXBElement(_ErrorTypeMessage_QNAME, String.class, ErrorType.class, value); + } } diff --git a/src/main/xsd/error.xsd b/src/main/xsd/error.xsd index 60ba6f2..486d26c 100644 --- a/src/main/xsd/error.xsd +++ b/src/main/xsd/error.xsd @@ -1,4 +1,8 @@ - + + + + + \ No newline at end of file diff --git a/src/main/xsd/types.xsd b/src/main/xsd/types.xsd index 1d2cf11..936f833 100644 --- a/src/main/xsd/types.xsd +++ b/src/main/xsd/types.xsd @@ -30,6 +30,13 @@ + + + + + + + diff --git a/src/test/java/pl/itcrowd/youtrack/api/rest/YoutrackUnmarshallerTest.java b/src/test/java/pl/itcrowd/youtrack/api/rest/YoutrackUnmarshallerTest.java new file mode 100644 index 0000000..d59dc34 --- /dev/null +++ b/src/test/java/pl/itcrowd/youtrack/api/rest/YoutrackUnmarshallerTest.java @@ -0,0 +1,88 @@ +package pl.itcrowd.youtrack.api.rest; + +import org.junit.Test; +import pl.itcrowd.youtrack.api.YoutrackUnmarshaller; +import pl.itcrowd.youtrack.api.exceptions.YoutrackAPIException; + +import javax.xml.bind.JAXBException; +import java.io.IOException; + +import static junit.framework.Assert.assertEquals; + +public class YoutrackUnmarshallerTest { + + @Test + public void unmarshalEmptyError() throws JAXBException, IOException + { +// Given + final String extendedErrorResponse = ""; + +// When + final String errorText = YoutrackUnmarshaller.unmarshalError(extendedErrorResponse); + +// THen + assertEquals("", errorText); + } + + @Test + public void unmarshalError() throws JAXBException, IOException + { +// Given + final String extendedErrorResponse = "Estimated completion time"; + +// When + final String errorText = YoutrackUnmarshaller.unmarshalError(extendedErrorResponse); + +// THen + assertEquals("Estimated completion time", errorText); + } + + @Test + public void unmarshalExtendedError() throws JAXBException, IOException + { +// Given + final String extendedErrorResponse = "Estimate task before starting work on itEstimated completion time"; + +// When + final String errorText = YoutrackUnmarshaller.unmarshalError(extendedErrorResponse); + +// Then + assertEquals("Estimate task before starting work on it", errorText); + } + + @Test + public void unmarshalExtendedErrorWithMessageOnly() throws JAXBException, IOException + { +// Given + final String extendedErrorResponse = "Estimate task before starting work on it"; + +// When + final String errorText = YoutrackUnmarshaller.unmarshalError(extendedErrorResponse); + +// Then + assertEquals("Estimate task before starting work on it", errorText); + } + + @Test + public void unmarshalExtendedErrorWithEmptyMessage() throws JAXBException, IOException + { +// Given + final String extendedErrorResponse = "Estimated completion time"; + +// When + final String errorText = YoutrackUnmarshaller.unmarshalError(extendedErrorResponse); + +// Then + assertEquals("", errorText); + } + + @Test(expected = YoutrackAPIException.class) + public void unmarshalExtendedErrorWithoutMessage() throws JAXBException, IOException + { +// Given + final String extendedErrorResponse = "Estimated completion time"; + +// When + YoutrackUnmarshaller.unmarshalError(extendedErrorResponse); + } +}