diff --git a/src/main/java/com/nulabinc/backlog4j/BacklogClient.java b/src/main/java/com/nulabinc/backlog4j/BacklogClient.java index ea0dc58e..a4816718 100644 --- a/src/main/java/com/nulabinc/backlog4j/BacklogClient.java +++ b/src/main/java/com/nulabinc/backlog4j/BacklogClient.java @@ -9,7 +9,7 @@ * @author nulab-inc */ public interface BacklogClient extends ProjectMethods, IssueMethods, SpaceMethods, - WikiMethods, ResolutionMethods, StatusMethods, PriorityMethods, UserMethods, + WikiMethods, DocumentMethods, ResolutionMethods, StatusMethods, PriorityMethods, UserMethods, StarMethods, NotificationMethods, GitMethods, PullRequestMethods, GroupMethods, WebhookMethods, WatchingMethods, BacklogUrlSupport { diff --git a/src/main/java/com/nulabinc/backlog4j/BacklogClientImpl.java b/src/main/java/com/nulabinc/backlog4j/BacklogClientImpl.java index 75cedb37..fd58dfc6 100644 --- a/src/main/java/com/nulabinc/backlog4j/BacklogClientImpl.java +++ b/src/main/java/com/nulabinc/backlog4j/BacklogClientImpl.java @@ -1105,4 +1105,22 @@ public void markAsCheckedUserWatches(Object numericUserId) throws BacklogExcepti post(buildEndpoint("users/" + numericUserId + "/watchings/markAsChecked")); } + @Override + public ResponseList getDocuments(GetDocumentsParams params) { + ResponseList documents = factory.createDocumentList(get(buildEndpoint("documents"), params)); + return documents; + } + + @Override + public Document getDocument(String documentId) { + return factory.createDocument(get(buildEndpoint("documents/" + documentId))); + } + + @Override + public AttachmentData downloadDocumentAttachment(String documentId, long attachmentId) { + BacklogHttpResponse backlogHttpResponse = get(backlogEndPointSupport.getDocumentAttachmentEndpoint(documentId, attachmentId)); + String filename = backlogHttpResponse.getFileNameFromContentDisposition(); + InputStream inputStream = backlogHttpResponse.asInputStream(); + return new AttachmentDataImpl(filename, inputStream); + } } diff --git a/src/main/java/com/nulabinc/backlog4j/BacklogEndPointSupport.java b/src/main/java/com/nulabinc/backlog4j/BacklogEndPointSupport.java index 6c2c87d1..312376a7 100644 --- a/src/main/java/com/nulabinc/backlog4j/BacklogEndPointSupport.java +++ b/src/main/java/com/nulabinc/backlog4j/BacklogEndPointSupport.java @@ -102,4 +102,16 @@ public String getPullRequestAttachmentEndpoint(Object projectIdOrKey, Object rep protected String buildEndpoint(String connection) { return configure.getRestBaseURL() + "/" + connection; } + + /** + * Returns the endpoint of Document page's attachment file. + * + * @param documentId the Document page identifier + * @param attachmentId the attachment identifier + * @return the endpoint + * @throws BacklogException + */ + public String getDocumentAttachmentEndpoint(String documentId, long attachmentId) throws BacklogException { + return buildEndpoint("documents/" + documentId + "/attachments/" + attachmentId); + } } diff --git a/src/main/java/com/nulabinc/backlog4j/Document.java b/src/main/java/com/nulabinc/backlog4j/Document.java new file mode 100644 index 00000000..baae6cb8 --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/Document.java @@ -0,0 +1,40 @@ +package com.nulabinc.backlog4j; + +import java.util.Date; +import java.util.List; + +/** + * The interface for Backlog Document page data. + * + * @author nulab-inc + */ +public interface Document { + + String getId(); + + long getProjectId(); + + String getProjectIdAsString(); + + String getTitle(); + + String getJson(); + + String getPlain(); + + long getStatusId(); + + String getEmoji(); + + List getTags(); + + List getAttachments(); + + User getCreatedUser(); + + Date getCreated(); + + User getUpdatedUser(); + + Date getUpdated(); +} diff --git a/src/main/java/com/nulabinc/backlog4j/DocumentTag.java b/src/main/java/com/nulabinc/backlog4j/DocumentTag.java new file mode 100644 index 00000000..27c0bbd1 --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/DocumentTag.java @@ -0,0 +1,15 @@ +package com.nulabinc.backlog4j; + +/** + * The interface for Backlog Document page's tag data. + * + * @author nulab-inc + */ +public interface DocumentTag { + + long getId(); + + String getIdAsString(); + + String getName(); +} diff --git a/src/main/java/com/nulabinc/backlog4j/api/DocumentMethods.java b/src/main/java/com/nulabinc/backlog4j/api/DocumentMethods.java new file mode 100644 index 00000000..ed63e7f5 --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/api/DocumentMethods.java @@ -0,0 +1,41 @@ +package com.nulabinc.backlog4j.api; + +import com.nulabinc.backlog4j.AttachmentData; +import com.nulabinc.backlog4j.Document; +import com.nulabinc.backlog4j.ResponseList; +import com.nulabinc.backlog4j.api.option.GetDocumentsParams; + +/** + * Executes Backlog Document APIs. + * + * @author nulab-inc + */ +public interface DocumentMethods { + + /** + * Returns Document pages in the project. + * + * @param params the finding document parameters + * @return the document pages in a list + */ + ResponseList getDocuments(GetDocumentsParams params); + + + /** + * Returns the Document page. + * + * @param documentId the Document page identifier + * @return the Document page + */ + Document getDocument(String documentId); + + /** + * Downloads the Document page's attachment file. + * + * @param documentId the Document page identifier + * @param attachmentId the attachment file identifier + * @return downloaded file data + */ + AttachmentData downloadDocumentAttachment(String documentId, long attachmentId); + +} diff --git a/src/main/java/com/nulabinc/backlog4j/api/option/GetDocumentsParams.java b/src/main/java/com/nulabinc/backlog4j/api/option/GetDocumentsParams.java new file mode 100644 index 00000000..fbb37f5b --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/api/option/GetDocumentsParams.java @@ -0,0 +1,89 @@ +package com.nulabinc.backlog4j.api.option; + +import com.nulabinc.backlog4j.BacklogException; +import com.nulabinc.backlog4j.http.NameValuePair; + +import java.util.List; + +/** + * Parameters for get document page API. + * + * @author nulab-inc + */ +public class GetDocumentsParams extends GetParams { + + private SortKey sort; + private Order order; + + public enum SortKey { + Created("created"), + Updated("updated"); + + SortKey(String value) { + this.value = value; + } + + public String getStrValue() { + return value; + } + + private final String value; + } + + public enum Order { + Asc("asc"), + Desc("desc"); + + Order(String value) { + this.value = value; + } + + public String getStrValue() { + return value; + } + + private final String value; + } + + public GetDocumentsParams(List projectIds, long offset) { + parameters.add(new NameValuePair("offset", String.valueOf(offset))); + for (Long projectId : projectIds) { + parameters.add(new NameValuePair("projectId[]", projectId.toString())); + } + } + + public GetDocumentsParams keyword(String keyword) throws BacklogException { + parameters.add(new NameValuePair("keyword", keyword)); + return this; + } + + public GetDocumentsParams sort(SortKey sort) { + parameters.add(new NameValuePair("sort", sort.getStrValue())); + this.sort = sort; + return this; + } + + public GetDocumentsParams order(Order order) { + parameters.add(new NameValuePair("order", order.getStrValue())); + this.order = order; + return this; + } + + public GetDocumentsParams offset(long offset) { + parameters.add(new NameValuePair("offset", String.valueOf(offset))); + return this; + } + + public GetDocumentsParams count(long count) { + parameters.add(new NameValuePair("count", String.valueOf(count))); + return this; + } + + public SortKey getSort() { + return sort; + } + + public Order getOrder() { + return order; + } +} diff --git a/src/main/java/com/nulabinc/backlog4j/internal/InternalFactory.java b/src/main/java/com/nulabinc/backlog4j/internal/InternalFactory.java index 0c3852b6..33efba5a 100644 --- a/src/main/java/com/nulabinc/backlog4j/internal/InternalFactory.java +++ b/src/main/java/com/nulabinc/backlog4j/internal/InternalFactory.java @@ -130,4 +130,8 @@ public interface InternalFactory { ResponseList createWatchList(BacklogHttpResponse res) throws BacklogException; + ResponseList createDocumentList(BacklogHttpResponse res) throws BacklogException; + + Document createDocument(BacklogHttpResponse res) throws BacklogException; + } diff --git a/src/main/java/com/nulabinc/backlog4j/internal/json/DocumentJSONImpl.java b/src/main/java/com/nulabinc/backlog4j/internal/json/DocumentJSONImpl.java new file mode 100644 index 00000000..27bbfb58 --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/internal/json/DocumentJSONImpl.java @@ -0,0 +1,179 @@ +package com.nulabinc.backlog4j.internal.json; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.nulabinc.backlog4j.*; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +/** + * @author nulab-inc + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class DocumentJSONImpl implements Document { + + private String id; + private long projectId; + private String title; + @JsonDeserialize(using = JacksonCustomJsonDeserializer.class) + private String json; + private String plain; + private long statusId; + private String emoji; + @JsonDeserialize(as = DocumentTagJSONImpl[].class) + private DocumentTag[] tags; + @JsonDeserialize(as = AttachmentJSONImpl[].class) + private Attachment[] attachments; + + @JsonDeserialize(as = UserJSONImpl.class) + private User createdUser; + @JsonDeserialize(using = JacksonCustomDateDeserializer.class) + private Date created; + @JsonDeserialize(as = UserJSONImpl.class) + private User updatedUser; + @JsonDeserialize(using = JacksonCustomDateDeserializer.class) + private Date updated; + + @Override + public String getId() { + return this.id; + } + + @Override + public String getTitle() { + return this.title; + } + + @Override + public String getJson() { + return this.json; + } + + @Override + public String getPlain() { + return this.plain; + } + + @Override + public long getStatusId() { + return this.statusId; + } + + @Override + public String getEmoji() { + return this.emoji; + } + + @Override + public List getTags() { + return Arrays.asList(this.tags); + } + + @Override + public List getAttachments() { + return Arrays.asList(this.attachments); + } + + @Override + public long getProjectId() { + return projectId; + } + + @Override + public String getProjectIdAsString() { + return String.valueOf(this.projectId); + } + + @Override + public User getCreatedUser() { + return createdUser; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public User getUpdatedUser() { + return updatedUser; + } + + @Override + public Date getUpdated() { + return updated; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj.getClass() != getClass()) { + return false; + } + DocumentJSONImpl rhs = (DocumentJSONImpl) obj; + return new EqualsBuilder() + .append(this.id, rhs.id) + .append(this.projectId, rhs.projectId) + .append(this.title, rhs.title) + .append(this.json, rhs.json) + .append(this.plain, rhs.plain) + .append(this.statusId, rhs.statusId) + .append(this.emoji, rhs.emoji) + .append(this.tags, rhs.tags) + .append(this.attachments, rhs.attachments) + .append(this.createdUser, rhs.createdUser) + .append(this.created, rhs.created) + .append(this.updatedUser, rhs.updatedUser) + .append(this.updated, rhs.updated) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder() + .append(id) + .append(projectId) + .append(title) + .append(json) + .append(plain) + .append(statusId) + .append(emoji) + .append(tags) + .append(attachments) + .append(createdUser) + .append(created) + .append(updatedUser) + .append(updated) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", id) + .append("projectId", projectId) + .append("title", title) + .append("json", json) + .append("plain", plain) + .append("statusId", statusId) + .append("emoji", emoji) + .append("tags", tags) + .append("attachments", attachments) + .append("createdUser", createdUser) + .append("created", created) + .append("updatedUser", updatedUser) + .append("updated", updated) + .toString(); + } +} diff --git a/src/main/java/com/nulabinc/backlog4j/internal/json/DocumentTagJSONImpl.java b/src/main/java/com/nulabinc/backlog4j/internal/json/DocumentTagJSONImpl.java new file mode 100644 index 00000000..5607a654 --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/internal/json/DocumentTagJSONImpl.java @@ -0,0 +1,67 @@ +package com.nulabinc.backlog4j.internal.json; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.nulabinc.backlog4j.DocumentTag; +import com.nulabinc.backlog4j.WikiTag; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +/** + * @author nulab-inc + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class DocumentTagJSONImpl implements DocumentTag { + + private long id; + private String name; + + @Override + public long getId() { + return this.id; + } + + @Override + public String getIdAsString() { + return String.valueOf(this.id); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj.getClass() != getClass()) { + return false; + } + DocumentTagJSONImpl rhs = (DocumentTagJSONImpl) obj; + return new EqualsBuilder() + .append(this.id, rhs.id) + .append(this.name, rhs.name) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder() + .append(id) + .append(name) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", id) + .append("name", name) + .toString(); + } +} diff --git a/src/main/java/com/nulabinc/backlog4j/internal/json/InternalFactoryJSONImpl.java b/src/main/java/com/nulabinc/backlog4j/internal/json/InternalFactoryJSONImpl.java index 8b15006b..2c95e178 100644 --- a/src/main/java/com/nulabinc/backlog4j/internal/json/InternalFactoryJSONImpl.java +++ b/src/main/java/com/nulabinc/backlog4j/internal/json/InternalFactoryJSONImpl.java @@ -547,6 +547,16 @@ public ResponseList createWatchList(BacklogHttpResponse res) throws Backl return createObjectList(res.asString(), Watch.class, WatchJSONImpl[].class); } + @Override + public ResponseList createDocumentList(BacklogHttpResponse res) throws BacklogException { + return createObjectList(res.asString(), Document.class, DocumentJSONImpl[].class); + } + + @Override + public Document createDocument(BacklogHttpResponse res) throws BacklogException { + return createObject(res.asString(), Document.class, DocumentJSONImpl.class); + } + public Watch createWatch(String resStr) throws BacklogException { return createObject(resStr, Watch.class, WatchJSONImpl.class); } diff --git a/src/main/java/com/nulabinc/backlog4j/internal/json/JacksonCustomJsonDeserializer.java b/src/main/java/com/nulabinc/backlog4j/internal/json/JacksonCustomJsonDeserializer.java new file mode 100644 index 00000000..273d501c --- /dev/null +++ b/src/main/java/com/nulabinc/backlog4j/internal/json/JacksonCustomJsonDeserializer.java @@ -0,0 +1,23 @@ +package com.nulabinc.backlog4j.internal.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; + +/** + * @author nulab-inc + */ +public class JacksonCustomJsonDeserializer extends JsonDeserializer { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + public String deserialize(JsonParser parser, DeserializationContext context) throws IOException { + return mapper.writeValueAsString(parser.readValueAsTree()); + } +} + +