Skip to content

Commit f312a0b

Browse files
committed
fix: #9 Fix issue with 500 error beign shown to user when space cannot be found by key.
1 parent 76143bd commit f312a0b

15 files changed

+200
-123
lines changed

plugins/.DS_Store

-6 KB
Binary file not shown.

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/action/QuickExportDiagramActionController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.github.vogoltsov.vp.plugins.confluence.action;
22

3-
import com.github.vogoltsov.vp.plugins.confluence.dialog.ExportDiagramToConfluenceDialog;
43
import com.github.vogoltsov.vp.plugins.common.util.ExceptionUtils;
4+
import com.github.vogoltsov.vp.plugins.confluence.dialog.ExportDiagramToConfluenceDialog;
55
import com.github.vogoltsov.vp.plugins.confluence.util.vp.DiagramExportUtils;
66
import com.vp.plugin.ApplicationManager;
77
import com.vp.plugin.action.VPAction;

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/client/ConfluenceAttachmentRepository.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.github.vogoltsov.vp.plugins.confluence.client;
22

3+
import com.fasterxml.jackson.databind.JsonNode;
34
import com.github.vogoltsov.vp.plugins.confluence.client.dto.CreateAttachmentResponse;
45
import com.github.vogoltsov.vp.plugins.confluence.client.dto.ListAttachmentsResponse;
56
import com.github.vogoltsov.vp.plugins.confluence.client.model.Attachment;
6-
import com.github.vogoltsov.vp.plugins.confluence.util.UnirestUtils;
77

88
import java.io.ByteArrayInputStream;
99
import java.util.Collections;
@@ -61,9 +61,11 @@ private List<Attachment> findAllAttachments(String pageId) {
6161
.routeParam("pageId", pageId)
6262
.queryString("expand", "container,container.space")
6363
.queryString("limit", "100")
64-
.asObject(ListAttachmentsResponse.class)
65-
.ifFailure(UnirestUtils::handleRequestFailure)
66-
.mapBody(ListAttachmentsResponse::getResults);
64+
.asObject(JsonNode.class)
65+
.ifFailure(ConfluenceClient.getInstance()::handleFailureResponse)
66+
.mapBody(ConfluenceClient.getInstance().map(ListAttachmentsResponse.class).andThen(
67+
ListAttachmentsResponse::getResults
68+
));
6769
}
6870

6971

@@ -74,9 +76,11 @@ public Attachment create(String pageId, String title, byte[] data) {
7476
return ConfluenceClient.getInstance().post("/rest/api/content/{pageId}/child/attachment")
7577
.routeParam("pageId", pageId)
7678
.field("file", new ByteArrayInputStream(data), title)
77-
.asObject(CreateAttachmentResponse.class)
78-
.ifFailure(UnirestUtils::handleRequestFailure)
79-
.mapBody(createAttachmentResponse -> createAttachmentResponse.getResults().get(0));
79+
.asObject(JsonNode.class)
80+
.ifFailure(ConfluenceClient.getInstance()::handleFailureResponse)
81+
.mapBody(ConfluenceClient.getInstance().map(CreateAttachmentResponse.class).andThen(
82+
createAttachmentResponse -> createAttachmentResponse.getResults().get(0)
83+
));
8084
}
8185

8286
/**
@@ -87,9 +91,9 @@ public Attachment update(String pageId, String attachmentId, String title, byte[
8791
.routeParam("pageId", pageId)
8892
.routeParam("attachmentId", attachmentId)
8993
.field("file", new ByteArrayInputStream(data), title)
90-
.asObject(Attachment.class)
91-
.ifFailure(UnirestUtils::handleRequestFailure)
92-
.getBody();
94+
.asObject(JsonNode.class)
95+
.ifFailure(ConfluenceClient.getInstance()::handleFailureResponse)
96+
.mapBody(ConfluenceClient.getInstance().map(Attachment.class));
9397
}
9498

9599

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/client/ConfluenceClient.java

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
package com.github.vogoltsov.vp.plugins.confluence.client;
22

3-
import com.github.vogoltsov.vp.plugins.confluence.client.model.Space;
4-
import com.github.vogoltsov.vp.plugins.confluence.util.UnirestUtils;
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.DeserializationFeature;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
8+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
9+
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
10+
import com.github.vogoltsov.vp.plugins.confluence.util.RemoteAPIException;
11+
import com.github.vogoltsov.vp.plugins.confluence.util.RemoteParseException;
512
import com.github.vogoltsov.vp.plugins.confluence.util.cql.CQLQuery;
613
import lombok.Getter;
714
import lombok.Setter;
15+
import unirest.Config;
816
import unirest.GetRequest;
917
import unirest.HttpRequestWithBody;
18+
import unirest.HttpResponse;
19+
import unirest.JacksonObjectMapper;
1020
import unirest.UnirestInstance;
1121

1222
import java.nio.charset.StandardCharsets;
23+
import java.util.Optional;
24+
import java.util.function.Function;
1325

1426
/**
1527
* @author Vitaly Ogoltsov &lt;vitaly.ogoltsov@me.com&gt;
@@ -21,7 +33,10 @@ public static ConfluenceClient getInstance() {
2133
}
2234

2335

24-
private final UnirestInstance unirest = UnirestUtils.configure();
36+
@Getter
37+
private final ObjectMapper objectMapper;
38+
@Getter
39+
private final UnirestInstance unirest;
2540

2641

2742
@Getter
@@ -37,6 +52,26 @@ public static ConfluenceClient getInstance() {
3752

3853

3954
public ConfluenceClient() {
55+
this.objectMapper = initObjectMapper();
56+
this.unirest = initUnirest();
57+
}
58+
59+
private ObjectMapper initObjectMapper() {
60+
ObjectMapper objectMapper = new ObjectMapper();
61+
objectMapper.registerModule(new ParameterNamesModule());
62+
objectMapper.registerModule(new Jdk8Module());
63+
objectMapper.registerModule(new JavaTimeModule());
64+
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
65+
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
66+
return objectMapper;
67+
}
68+
69+
private UnirestInstance initUnirest() {
70+
Config config = new Config();
71+
config.setObjectMapper(new JacksonObjectMapper(this.objectMapper));
72+
config.setDefaultHeader("accept", "application/json");
73+
config.setDefaultResponseEncoding(StandardCharsets.UTF_8.name());
74+
return new UnirestInstance(config);
4075
}
4176

4277

@@ -53,25 +88,23 @@ public boolean isConfigured() {
5388
*/
5489
public void testConnection() {
5590
get("/rest/api/user/current")
56-
.asJson()
57-
.ifFailure(UnirestUtils::handleRequestFailure);
58-
}
59-
60-
public Space findSpaceById(String key) {
61-
return get("/rest/api/space/{key}")
62-
.routeParam("key", key)
63-
.asObject(Space.class)
64-
.ifFailure(UnirestUtils::handleRequestFailure)
65-
.getBody();
91+
.asObject(JsonNode.class)
92+
.ifFailure(this::handleFailureResponse);
6693
}
6794

6895

96+
/**
97+
* Creates a generic Confluence search request.
98+
*/
6999
GetRequest search(CQLQuery cql) {
70100
return get("/rest/api/search")
71101
.queryString("cql", cql.getQueryString())
72102
.queryString("expand", String.join(",", cql.getExpandedProperties()));
73103
}
74104

105+
/**
106+
* Creates a generic Confluence GET request.
107+
*/
75108
GetRequest get(String uri) {
76109
GetRequest request = unirest.get(this.baseUrl + uri);
77110
request.header("accept", "application/json");
@@ -82,6 +115,9 @@ GetRequest get(String uri) {
82115
return request;
83116
}
84117

118+
/**
119+
* Creates a generic Confluence POST request.
120+
*/
85121
HttpRequestWithBody post(String uri) {
86122
HttpRequestWithBody request = unirest.post(this.baseUrl + uri);
87123
request.charset(StandardCharsets.UTF_8);
@@ -94,6 +130,44 @@ HttpRequestWithBody post(String uri) {
94130
}
95131

96132

133+
/**
134+
* Performs general failure handling for Confluence API responses.
135+
*/
136+
void handleFailureResponse(HttpResponse<JsonNode> response) {
137+
// parse confluence error format
138+
if (response.getBody() != null && response.getBody().has("statusCode")) {
139+
throw new RemoteAPIException(
140+
response.getBody().get("statusCode").intValue(),
141+
Optional.ofNullable(response.getBody())
142+
.map(body -> body.get("message"))
143+
.map(JsonNode::textValue)
144+
.orElse(null),
145+
Optional.ofNullable(response.getBody())
146+
.map(body -> body.get("reason"))
147+
.map(JsonNode::textValue)
148+
.orElse(null)
149+
);
150+
}
151+
// default http error
152+
String message = "Request failed with HTTP error code " + response.getStatus();
153+
String statusText = response.getStatusText();
154+
if (statusText != null && !statusText.isEmpty()) {
155+
message += ": " + statusText;
156+
}
157+
throw new RuntimeException(message);
158+
}
159+
160+
<T> Function<JsonNode, T> map(Class<T> clazz) {
161+
return jsonNode -> {
162+
try {
163+
return this.objectMapper.treeToValue(jsonNode, clazz);
164+
} catch (JsonProcessingException e) {
165+
throw new RemoteParseException(e);
166+
}
167+
};
168+
}
169+
170+
97171
private static class SingletonHolder {
98172
private static final ConfluenceClient INSTANCE = new ConfluenceClient();
99173
}

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/client/ConfluencePageRepository.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.github.vogoltsov.vp.plugins.confluence.client;
22

3+
import com.fasterxml.jackson.databind.JsonNode;
34
import com.github.vogoltsov.vp.plugins.confluence.client.dto.SearchResult;
45
import com.github.vogoltsov.vp.plugins.confluence.client.dto.SearchResults;
56
import com.github.vogoltsov.vp.plugins.confluence.client.model.Page;
6-
import com.github.vogoltsov.vp.plugins.confluence.util.UnirestUtils;
77
import com.github.vogoltsov.vp.plugins.confluence.util.cql.CQL;
88
import com.github.vogoltsov.vp.plugins.confluence.util.cql.CQLQuery;
99

@@ -58,16 +58,16 @@ private List<Page> search(CQLQuery cql) {
5858
// always expand content space
5959
cql.expand("content.space");
6060
return ConfluenceClient.getInstance().search(cql)
61-
.asObject(SearchResults.class)
62-
.ifFailure(UnirestUtils::handleRequestFailure)
63-
.mapBody(
61+
.asObject(JsonNode.class)
62+
.ifFailure(ConfluenceClient.getInstance()::handleFailureResponse)
63+
.mapBody(ConfluenceClient.getInstance().map(SearchResults.class).andThen(
6464
searchResults -> searchResults.getResults().stream()
6565
.filter(SearchResult.ContentSearchResult.class::isInstance)
6666
.map(searchResult -> ((SearchResult.ContentSearchResult) searchResult).getContent())
6767
.filter(Page.class::isInstance)
6868
.map(content -> (Page) content)
6969
.collect(Collectors.toList())
70-
);
70+
));
7171
}
7272

7373

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/client/ConfluenceSpaceRepository.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package com.github.vogoltsov.vp.plugins.confluence.client;
22

3+
import com.fasterxml.jackson.databind.JsonNode;
34
import com.github.vogoltsov.vp.plugins.confluence.client.dto.SearchResult;
45
import com.github.vogoltsov.vp.plugins.confluence.client.dto.SearchResults;
56
import com.github.vogoltsov.vp.plugins.confluence.client.model.Space;
6-
import com.github.vogoltsov.vp.plugins.confluence.util.UnirestUtils;
7+
import com.github.vogoltsov.vp.plugins.confluence.util.RemoteAPIException;
78
import com.github.vogoltsov.vp.plugins.confluence.util.cql.CQL;
89
import com.github.vogoltsov.vp.plugins.confluence.util.cql.CQLQuery;
910

11+
import java.util.Collections;
1012
import java.util.List;
13+
import java.util.Objects;
1114
import java.util.stream.Collectors;
1215

1316
/**
@@ -55,15 +58,24 @@ public Space findByKey(String spaceKey) {
5558

5659

5760
private List<Space> search(CQLQuery cql) {
58-
return ConfluenceClient.getInstance().search(cql)
59-
.asObject(SearchResults.class)
60-
.ifFailure(UnirestUtils::handleRequestFailure)
61-
.mapBody(
62-
searchResults -> searchResults.getResults().stream()
63-
.filter(SearchResult.SpaceSearchResult.class::isInstance)
64-
.map(searchResult -> ((SearchResult.SpaceSearchResult) searchResult).getSpace())
65-
.collect(Collectors.toList())
66-
);
61+
try {
62+
return ConfluenceClient.getInstance().search(cql)
63+
.asObject(JsonNode.class)
64+
.ifFailure(ConfluenceClient.getInstance()::handleFailureResponse)
65+
.mapBody(ConfluenceClient.getInstance().map(SearchResults.class).andThen(
66+
searchResults -> searchResults.getResults().stream()
67+
.filter(SearchResult.SpaceSearchResult.class::isInstance)
68+
.map(searchResult -> ((SearchResult.SpaceSearchResult) searchResult).getSpace())
69+
.collect(Collectors.toList())
70+
));
71+
} catch (RemoteAPIException e) {
72+
// this is a special case to mitigate bug in Confluence REST API
73+
// see https://jira.atlassian.com/browse/CONFSERVER-55445
74+
if (Objects.equals(e.getApiMessage(), "java.lang.IllegalArgumentException: parameters should not be empty")) {
75+
return Collections.emptyList();
76+
}
77+
throw e;
78+
}
6779
}
6880

6981

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/dialog/ConfluenceAttachmentChooserDialog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.github.vogoltsov.vp.plugins.confluence.dialog;
22

3+
import com.github.vogoltsov.vp.plugins.common.swing.ListTableModel;
34
import com.github.vogoltsov.vp.plugins.confluence.client.ConfluenceAttachmentRepository;
45
import com.github.vogoltsov.vp.plugins.confluence.client.model.Attachment;
56
import com.github.vogoltsov.vp.plugins.confluence.client.model.Page;
6-
import com.github.vogoltsov.vp.plugins.common.swing.ListTableModel;
77
import com.github.vogoltsov.vp.plugins.confluence.util.swing.SearchChooserDialog;
88
import lombok.RequiredArgsConstructor;
99

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/dialog/ConfluencePageChooserDialog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.github.vogoltsov.vp.plugins.confluence.dialog;
22

3+
import com.github.vogoltsov.vp.plugins.common.swing.ListTableModel;
34
import com.github.vogoltsov.vp.plugins.confluence.client.ConfluencePageRepository;
45
import com.github.vogoltsov.vp.plugins.confluence.client.model.Page;
56
import com.github.vogoltsov.vp.plugins.confluence.client.model.Space;
6-
import com.github.vogoltsov.vp.plugins.common.swing.ListTableModel;
77
import com.github.vogoltsov.vp.plugins.confluence.util.swing.SearchChooserDialog;
88
import lombok.RequiredArgsConstructor;
99

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/dialog/ConfluenceServerConnectionDialog.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.github.vogoltsov.vp.plugins.confluence.dialog;
22

3-
import com.github.vogoltsov.vp.plugins.confluence.client.ConfluenceClient;
4-
import com.github.vogoltsov.vp.plugins.common.util.ExceptionUtils;
53
import com.github.vogoltsov.vp.plugins.common.swing.ABaseDialog;
64
import com.github.vogoltsov.vp.plugins.common.swing.ButtonsPanel;
75
import com.github.vogoltsov.vp.plugins.common.swing.DocumentListenerAdapter;
86
import com.github.vogoltsov.vp.plugins.common.swing.HelpPanel;
7+
import com.github.vogoltsov.vp.plugins.common.util.ExceptionUtils;
8+
import com.github.vogoltsov.vp.plugins.confluence.client.ConfluenceClient;
99
import com.github.vogoltsov.vp.plugins.confluence.util.vp.ProjectUtils;
1010
import com.vp.plugin.ApplicationManager;
1111

@@ -22,7 +22,6 @@
2222
import javax.swing.SwingConstants;
2323
import java.awt.GridBagConstraints;
2424
import java.awt.GridBagLayout;
25-
import java.awt.event.ActionEvent;
2625
import java.net.MalformedURLException;
2726
import java.net.URL;
2827
import java.util.Optional;

plugins/confluence/src/main/java/com/github/vogoltsov/vp/plugins/confluence/dialog/ConfluenceSpaceChooserDialog.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.github.vogoltsov.vp.plugins.confluence.dialog;
22

3-
import com.github.vogoltsov.vp.plugins.confluence.client.ConfluenceSpaceRepository;
4-
import com.github.vogoltsov.vp.plugins.confluence.client.model.Space;
53
import com.github.vogoltsov.vp.plugins.common.swing.HelpPanel;
64
import com.github.vogoltsov.vp.plugins.common.swing.ListTableModel;
5+
import com.github.vogoltsov.vp.plugins.confluence.client.ConfluenceSpaceRepository;
6+
import com.github.vogoltsov.vp.plugins.confluence.client.model.Space;
77
import com.github.vogoltsov.vp.plugins.confluence.util.swing.SearchChooserDialog;
88

99
import java.util.Arrays;

0 commit comments

Comments
 (0)