diff --git a/pom.xml b/pom.xml index 4b8b074..17be54f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - pl.com.it-crowd.youtrack-rest-api + pl.com.it-crowd youtrack-rest-api 1.0.0-SNAPSHOT @@ -60,6 +60,7 @@ pl.com.it_crowd.youtrack.api.rest ${build.sourceDirectory} false + false @@ -74,4 +75,4 @@ http://artifactory.it-crowd.com.pl/libs-snapshot-local - \ No newline at end of file + diff --git a/src/main/java/pl/com/it_crowd/youtrack/api/Issue.java b/src/main/java/pl/com/it_crowd/youtrack/api/Issue.java new file mode 100644 index 0000000..f60862f --- /dev/null +++ b/src/main/java/pl/com/it_crowd/youtrack/api/Issue.java @@ -0,0 +1,63 @@ +package pl.com.it_crowd.youtrack.api; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import pl.com.it_crowd.youtrack.api.rest.Issues; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Issue implements Serializable { +// ------------------------------ FIELDS ------------------------------ + + private static final Log log = LogFactory.getLog(Issue.class); + private List comments; + private Map fieldMap; + + private Issues.Issue issue; + +// --------------------------- CONSTRUCTORS --------------------------- + + public Issue(Issues.Issue issue) { + this.issue = issue; + fieldMap = new HashMap(); + comments = new ArrayList(); + for (Object o : issue.getFieldOrComment()) { + if (o instanceof Issues.Issue.Field) { + Issues.Issue.Field field = (Issues.Issue.Field) o; + fieldMap.put(field.getName(), field); + } else if (o instanceof Issues.Issue.Comment) { + comments.add((Issues.Issue.Comment) o); + } else { + log.warn("Object " + o + " is not Field nor Coment"); + } + } + } + +// -------------------------- OTHER METHODS -------------------------- + + public Issues.Issue.Field getField(String field) { + return fieldMap.get(field); + } + + public String getFieldValue(String fieldName) { + Issues.Issue.Field field = fieldMap.get(fieldName); + if (field == null) { + return null; + } + List values = field.getValues(); + return values.isEmpty() ? null : values.get(0).getContent(); + } + + public String getFieldValue(Fields fieldName) { + return getFieldValue(fieldName.name()); + } +// -------------------------- ENUMERATIONS -------------------------- + + public enum Fields { + summary + } +} diff --git a/src/main/java/pl/com/it_crowd/youtrack/api/rest/Issues.java b/src/main/java/pl/com/it_crowd/youtrack/api/rest/Issues.java index c6b0128..549ee8e 100644 --- a/src/main/java/pl/com/it_crowd/youtrack/api/rest/Issues.java +++ b/src/main/java/pl/com/it_crowd/youtrack/api/rest/Issues.java @@ -2,23 +2,19 @@ // 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. -// Generated on: 2011.12.05 at 08:56:01 AM CET +// Generated on: 2011.12.16 at 09:34:48 AM CET // package pl.com.it_crowd.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.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlElements; -import javax.xml.bind.annotation.XmlMixed; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlValue; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -100,7 +96,9 @@ import java.util.List; * */ @XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = {"issue"}) +@XmlType(name = "", propOrder = { + "issue" +}) @XmlRootElement(name = "issues") public class Issues { @@ -126,8 +124,7 @@ public class Issues { * Objects of the following type(s) are allowed in the list * {@link Issues.Issue } */ - public List getIssue() - { + public List getIssue() { if (issue == null) { issue = new ArrayList(); } @@ -202,12 +199,16 @@ public class Issues { * */ @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"fieldOrComment"}) + @XmlType(name = "", propOrder = { + "fieldOrComment" + }) public static class Issue { - @XmlElements({@XmlElement(name = "field", type = Issues.Issue.Field.class), @XmlElement(name = "comment", type = Issues.Issue.Comment.class)}) + @XmlElements({ + @XmlElement(name = "field", type = Issues.Issue.Field.class), + @XmlElement(name = "comment", type = Issues.Issue.Comment.class) + }) protected List fieldOrComment; - @XmlAttribute protected String id; @@ -232,8 +233,7 @@ public class Issues { * {@link Issues.Issue.Field } * {@link Issues.Issue.Comment } */ - public List getFieldOrComment() - { + public List getFieldOrComment() { if (fieldOrComment == null) { fieldOrComment = new ArrayList(); } @@ -246,8 +246,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getId() - { + public String getId() { return id; } @@ -257,8 +256,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setId(String value) - { + public void setId(String value) { this.id = value; } @@ -298,34 +296,28 @@ public class Issues { * */ @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"replies", "value"}) + @XmlType(name = "", propOrder = { + "replies", + "value" + }) public static class Comment { protected String replies; - protected Issues.Issue.Comment.Value value; - @XmlAttribute protected String id; - @XmlAttribute protected String author; - @XmlAttribute protected String issueId; - @XmlAttribute protected String deleted; - @XmlAttribute protected String text; - @XmlAttribute protected String shownForIssueAuthor; - @XmlAttribute protected String created; - @XmlAttribute protected String name; @@ -335,8 +327,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getReplies() - { + public String getReplies() { return replies; } @@ -346,8 +337,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setReplies(String value) - { + public void setReplies(String value) { this.replies = value; } @@ -357,8 +347,7 @@ public class Issues { * @return possible object is * {@link Issues.Issue.Comment.Value } */ - public Issues.Issue.Comment.Value getValue() - { + public Issues.Issue.Comment.Value getValue() { return value; } @@ -368,8 +357,7 @@ public class Issues { * @param value allowed object is * {@link Issues.Issue.Comment.Value } */ - public void setValue(Issues.Issue.Comment.Value value) - { + public void setValue(Issues.Issue.Comment.Value value) { this.value = value; } @@ -379,8 +367,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getId() - { + public String getId() { return id; } @@ -390,8 +377,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setId(String value) - { + public void setId(String value) { this.id = value; } @@ -401,8 +387,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getAuthor() - { + public String getAuthor() { return author; } @@ -412,8 +397,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setAuthor(String value) - { + public void setAuthor(String value) { this.author = value; } @@ -423,8 +407,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getIssueId() - { + public String getIssueId() { return issueId; } @@ -434,8 +417,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setIssueId(String value) - { + public void setIssueId(String value) { this.issueId = value; } @@ -445,8 +427,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getDeleted() - { + public String getDeleted() { return deleted; } @@ -456,8 +437,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setDeleted(String value) - { + public void setDeleted(String value) { this.deleted = value; } @@ -467,8 +447,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getText() - { + public String getText() { return text; } @@ -478,8 +457,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setText(String value) - { + public void setText(String value) { this.text = value; } @@ -489,8 +467,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getShownForIssueAuthor() - { + public String getShownForIssueAuthor() { return shownForIssueAuthor; } @@ -500,8 +477,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setShownForIssueAuthor(String value) - { + public void setShownForIssueAuthor(String value) { this.shownForIssueAuthor = value; } @@ -511,8 +487,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getCreated() - { + public String getCreated() { return created; } @@ -522,8 +497,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setCreated(String value) - { + public void setCreated(String value) { this.created = value; } @@ -533,8 +507,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getName() - { + public String getName() { return name; } @@ -544,8 +517,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setName(String value) - { + public void setName(String value) { this.name = value; } @@ -566,15 +538,15 @@ public class Issues { * */ @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"value"}) + @XmlType(name = "", propOrder = { + "value" + }) public static class Value { @XmlValue protected String value; - @XmlAttribute protected String type; - @XmlAttribute protected String role; @@ -584,8 +556,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getValue() - { + public String getValue() { return value; } @@ -595,8 +566,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setValue(String value) - { + public void setValue(String value) { this.value = value; } @@ -606,8 +576,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getType() - { + public String getType() { return type; } @@ -617,8 +586,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setType(String value) - { + public void setType(String value) { this.type = value; } @@ -628,8 +596,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getRole() - { + public String getRole() { return role; } @@ -639,11 +606,12 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setRole(String value) - { + public void setRole(String value) { this.role = value; } + } + } /** @@ -674,43 +642,41 @@ public class Issues { * */ @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"content"}) + @XmlType(name = "", propOrder = { + "values" + }) public static class Field { - @XmlElementRef(name = "value", type = JAXBElement.class) - @XmlMixed - protected List content; - + @XmlElement(name = "value") + protected List values; @XmlAttribute protected String name; /** - * Gets the value of the content property. + * Gets the value of the values 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. + * This is why there is not a set method for the values property. *

*

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

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

*

*

* Objects of the following type(s) are allowed in the list - * {@link String } - * {@link JAXBElement }{@code <}{@link Issues.Issue.Field.Value }{@code >} + * {@link Issues.Issue.Field.Value } */ - public List getContent() - { - if (content == null) { - content = new ArrayList(); + public List getValues() { + if (values == null) { + values = new ArrayList(); } - return this.content; + return this.values; } /** @@ -719,8 +685,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getName() - { + public String getName() { return name; } @@ -730,8 +695,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setName(String value) - { + public void setName(String value) { this.name = value; } @@ -752,23 +716,45 @@ public class Issues { * */ @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "") + @XmlType(name = "", propOrder = { + "content" + }) public static class Value { + @XmlValue + protected String content; @XmlAttribute protected String type; - @XmlAttribute protected String role; /** + * Gets the value of the content property. + * + * @return possible object is + * {@link String } + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value allowed object is + * {@link String } + */ + public void setContent(String value) { + this.content = value; + } + + /** * Gets the value of the type property. * * @return possible object is * {@link String } */ - public String getType() - { + public String getType() { return type; } @@ -778,8 +764,7 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setType(String value) - { + public void setType(String value) { this.type = value; } @@ -789,8 +774,7 @@ public class Issues { * @return possible object is * {@link String } */ - public String getRole() - { + public String getRole() { return role; } @@ -800,11 +784,14 @@ public class Issues { * @param value allowed object is * {@link String } */ - public void setRole(String value) - { + public void setRole(String value) { this.role = value; } + } + } + } + } diff --git a/src/main/java/pl/com/it_crowd/youtrack/api/rest/ObjectFactory.java b/src/main/java/pl/com/it_crowd/youtrack/api/rest/ObjectFactory.java index aaa5260..b10762e 100644 --- a/src/main/java/pl/com/it_crowd/youtrack/api/rest/ObjectFactory.java +++ b/src/main/java/pl/com/it_crowd/youtrack/api/rest/ObjectFactory.java @@ -2,15 +2,12 @@ // 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. -// Generated on: 2011.12.05 at 08:56:01 AM CET +// Generated on: 2011.12.16 at 09:34:48 AM CET // package pl.com.it_crowd.youtrack.api.rest; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlRegistry; -import javax.xml.namespace.QName; /** * This object contains factory methods for each @@ -28,69 +25,52 @@ import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { - private final static QName _IssuesIssueFieldValue_QNAME = new QName("", "value"); - /** * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: pl.com.it_crowd.youtrack.api.rest */ - public ObjectFactory() - { + public ObjectFactory() { } /** * Create an instance of {@link Issues.Issue.Comment.Value } */ - public Issues.Issue.Comment.Value createIssuesIssueCommentValue() - { + public Issues.Issue.Comment.Value createIssuesIssueCommentValue() { return new Issues.Issue.Comment.Value(); } /** - * Create an instance of {@link Issues.Issue.Comment } - */ - public Issues.Issue.Comment createIssuesIssueComment() - { - return new Issues.Issue.Comment(); - } - - /** * Create an instance of {@link Issues.Issue.Field } */ - public Issues.Issue.Field createIssuesIssueField() - { + public Issues.Issue.Field createIssuesIssueField() { return new Issues.Issue.Field(); } /** * Create an instance of {@link Issues } */ - public Issues createIssues() - { + public Issues createIssues() { return new Issues(); } /** - * Create an instance of {@link Issues.Issue } + * Create an instance of {@link Issues.Issue.Field.Value } */ - public Issues.Issue createIssuesIssue() - { - return new Issues.Issue(); + public Issues.Issue.Field.Value createIssuesIssueFieldValue() { + return new Issues.Issue.Field.Value(); } /** - * Create an instance of {@link Issues.Issue.Field.Value } + * Create an instance of {@link Issues.Issue.Comment } */ - public Issues.Issue.Field.Value createIssuesIssueFieldValue() - { - return new Issues.Issue.Field.Value(); + public Issues.Issue.Comment createIssuesIssueComment() { + return new Issues.Issue.Comment(); } /** - * Create an instance of {@link JAXBElement }{@code <}{@link Issues.Issue.Field.Value }{@code >}} + * Create an instance of {@link Issues.Issue } */ - @XmlElementDecl(namespace = "", name = "value", scope = Issues.Issue.Field.class) - public JAXBElement createIssuesIssueFieldValue(Issues.Issue.Field.Value value) - { - return new JAXBElement(_IssuesIssueFieldValue_QNAME, Issues.Issue.Field.Value.class, Issues.Issue.Field.class, value); + public Issues.Issue createIssuesIssue() { + return new Issues.Issue(); } + } diff --git a/src/main/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPI.java b/src/main/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPI.java index 9b64c5f..75b49c7 100644 --- a/src/main/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPI.java +++ b/src/main/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPI.java @@ -1,59 +1,82 @@ package pl.com.it_crowd.youtrack.api.rest; import com.gargoylesoftware.htmlunit.BrowserVersion; +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.HttpMethod; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.WebResponse; +import com.gargoylesoftware.htmlunit.html.DomNode; import com.gargoylesoftware.htmlunit.util.NameValuePair; +import com.gargoylesoftware.htmlunit.xml.XmlPage; +import org.apache.http.auth.AuthenticationException; +import pl.com.it_crowd.youtrack.api.Issue; import pl.com.it_crowd.youtrack.api.IssuesUnmarshaller; import javax.xml.bind.JAXBException; import java.io.IOException; import java.net.URL; import java.util.ArrayList; +import java.util.List; public class YoutrackAPI { +// ------------------------------ FIELDS ------------------------------ private String serviceLocation; private WebClient webClient; - public String getServiceLocation() - { - return serviceLocation; - } +// --------------------------- CONSTRUCTORS --------------------------- - public YoutrackAPI(String serviceLocation) - { + public YoutrackAPI(String serviceLocation) { this.serviceLocation = serviceLocation; this.webClient = new WebClient(BrowserVersion.FIREFOX_3_6); this.webClient.setJavaScriptEnabled(false); this.webClient.setCssEnabled(false); } - public YoutrackAPI(String serviceLocation, String username, String password) throws IOException - { + public YoutrackAPI(String serviceLocation, String username, String password) throws IOException, AuthenticationException { this(serviceLocation); login(username, password); } - public void login(String username, String password) throws IOException - { +// --------------------- GETTER / SETTER METHODS --------------------- + + public String getServiceLocation() { + return serviceLocation; + } + +// -------------------------- OTHER METHODS -------------------------- + + public void login(String username, String password) throws IOException, AuthenticationException { ArrayList requestParameters = new ArrayList(); requestParameters.add(new NameValuePair("login", username)); requestParameters.add(new NameValuePair("password", password)); WebRequest request = new WebRequest(new URL(serviceLocation + "/rest/user/login"), HttpMethod.POST); request.setRequestParameters(requestParameters); - WebResponse response = webClient.getPage(request).getWebResponse(); - System.out.println(response); - System.out.println(response.getContentAsString()); + try { + webClient.getPage(request); + } catch (FailingHttpStatusCodeException e) { + if (e.getStatusCode() == 403) { + DomNode error = ((XmlPage) webClient.getCurrentWindow().getEnclosedPage()).getFirstChild(); + if (error != null) { + throw new AuthenticationException(error.getTextContent()); + } + } + throw e; + } } - public Issues searchIssuesByProject(String project, String filter) throws JAXBException, IOException - { + public List searchIssuesByProject(String project, String filter) throws JAXBException, IOException { String url = serviceLocation + "/rest/issue/byproject/" + project + "?filter=" + filter; - return IssuesUnmarshaller.unmarshal(webClient.getPage(url).getWebResponse().getContentAsStream()); + WebResponse webResponse = webClient.getPage(url).getWebResponse(); +// System.out.println(webResponse.getContentAsString()); + List issues = IssuesUnmarshaller.unmarshal(webResponse.getContentAsStream()).getIssue(); + List result = new ArrayList(); + for (Issues.Issue issue : issues) { + result.add(new Issue(issue)); + } + return result; } } diff --git a/src/main/xjb/bindings.xjb b/src/main/xjb/bindings.xjb new file mode 100644 index 0000000..95d5880 --- /dev/null +++ b/src/main/xjb/bindings.xjb @@ -0,0 +1,18 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/xsd/issuesByProject.xsd b/src/main/xsd/issuesByProject.xsd index a8e4eb0..261031c 100644 --- a/src/main/xsd/issuesByProject.xsd +++ b/src/main/xsd/issuesByProject.xsd @@ -1,5 +1,5 @@ - + @@ -7,10 +7,10 @@ - + - + diff --git a/src/test/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPITest.java b/src/test/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPITest.java new file mode 100644 index 0000000..fa86ee8 --- /dev/null +++ b/src/test/java/pl/com/it_crowd/youtrack/api/rest/YoutrackAPITest.java @@ -0,0 +1,53 @@ +package pl.com.it_crowd.youtrack.api.rest; + +import junit.framework.Assert; +import org.apache.http.auth.AuthenticationException; +import org.junit.Test; +import pl.com.it_crowd.youtrack.api.Issue; + +import javax.xml.bind.JAXBException; +import java.io.IOException; +import java.util.List; + +/** + * This test requires setting JVM params youtrackUsername and youtrackPassword. + */ +public class YoutrackAPITest { +// -------------------------- OTHER METHODS -------------------------- + + @Test(expected = AuthenticationException.class) + public void loginFailureTest() throws IOException, AuthenticationException { + final String username = "someFakeLogin"; + final String password = "someFakePassword"; + new YoutrackAPI("http://youtrack.it-crowd.com.pl", username, password); + } + + @Test + public void loginTest() throws IOException, AuthenticationException { + final String username = getUsername(); + final String password = getPassword(); + new YoutrackAPI("http://youtrack.it-crowd.com.pl", username, password); + YoutrackAPI api = new YoutrackAPI("http://youtrack.it-crowd.com.pl"); + api.login(username, password); + } + + @Test + public void searchIssuesByProjectTest() throws IOException, AuthenticationException, JAXBException { + YoutrackAPI api = new YoutrackAPI("http://youtrack.it-crowd.com.pl", getUsername(), getPassword()); + List issues = api.searchIssuesByProject("SM", null); + Assert.assertTrue(!issues.isEmpty()); + for (Issue issue : issues) { + String summary = issue.getFieldValue(Issue.Fields.summary); + Assert.assertNotNull(summary); + Assert.assertTrue(!"".equals(summary.trim())); + } + } + + private String getPassword() { + return System.getProperty("youtrackPassword"); + } + + private String getUsername() { + return System.getProperty("youtrackUsername"); + } +}