1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.atlassian.jira.rest.client.internal.async;
17
18 import com.atlassian.httpclient.api.HttpClient;
19 import com.atlassian.jira.rest.client.api.IssueRestClient;
20 import com.atlassian.jira.rest.client.api.RestClientException;
21 import com.atlassian.jira.rest.client.api.SearchRestClient;
22 import com.atlassian.jira.rest.client.api.domain.Filter;
23 import com.atlassian.jira.rest.client.api.domain.SearchResult;
24 import com.atlassian.jira.rest.client.internal.json.FilterJsonParser;
25 import com.atlassian.jira.rest.client.internal.json.GenericJsonArrayParser;
26 import com.atlassian.jira.rest.client.internal.json.SearchResultJsonParser;
27 import com.atlassian.util.concurrent.Promise;
28 import com.google.common.base.Function;
29 import com.google.common.base.Joiner;
30 import com.google.common.collect.ImmutableList;
31 import com.google.common.collect.Iterables;
32 import org.apache.commons.lang3.StringUtils;
33 import org.codehaus.jettison.json.JSONException;
34 import org.codehaus.jettison.json.JSONObject;
35
36 import javax.annotation.Nullable;
37 import javax.ws.rs.core.UriBuilder;
38 import java.net.URI;
39 import java.util.Set;
40
41 import static com.atlassian.jira.rest.client.api.IssueRestClient.Expandos.NAMES;
42 import static com.atlassian.jira.rest.client.api.IssueRestClient.Expandos.SCHEMA;
43
44
45
46
47
48
49 public class AsynchronousSearchRestClient extends AbstractAsynchronousRestClient implements SearchRestClient {
50
51 private static final Function<IssueRestClient.Expandos, String> EXPANDO_TO_PARAM = new Function<IssueRestClient.Expandos, String>() {
52 @Override
53 public String apply(IssueRestClient.Expandos from) {
54 return from.name().toLowerCase();
55 }
56 };
57
58 private static final String START_AT_ATTRIBUTE = "startAt";
59 private static final String MAX_RESULTS_ATTRIBUTE = "maxResults";
60 private static final int MAX_JQL_LENGTH_FOR_HTTP_GET = 500;
61 private static final String JQL_ATTRIBUTE = "jql";
62 private static final String FILTER_FAVOURITE_PATH = "filter/favourite";
63 private static final String FILTER_PATH_FORMAT = "filter/%s";
64 private static final String SEARCH_URI_PREFIX = "search";
65 private static final String EXPAND_ATTRIBUTE = "expand";
66 private static final String FIELDS_ATTRIBUTE = "fields";
67
68 private final SearchResultJsonParser searchResultJsonParser = new SearchResultJsonParser();
69 private final FilterJsonParser filterJsonParser = new FilterJsonParser();
70 private final GenericJsonArrayParser<Filter> filtersParser = GenericJsonArrayParser.create(new FilterJsonParser());
71
72 private final URI searchUri;
73 private final URI favouriteUri;
74 private final URI baseUri;
75
76 public AsynchronousSearchRestClient(final URI baseUri, final HttpClient asyncHttpClient) {
77 super(asyncHttpClient);
78 this.baseUri = baseUri;
79 this.searchUri = UriBuilder.fromUri(baseUri).path(SEARCH_URI_PREFIX).build();
80 this.favouriteUri = UriBuilder.fromUri(baseUri).path(FILTER_FAVOURITE_PATH).build();
81 }
82
83 @Override
84 public Promise<SearchResult> searchJql(@Nullable String jql) {
85 return searchJql(jql, null, null, null);
86 }
87
88 public Promise<SearchResult> searchJql(@Nullable String jql, @Nullable Integer maxResults, @Nullable Integer startAt, @Nullable Set<String> fields) {
89 final Iterable<String> expandosValues = Iterables.transform(ImmutableList.of(SCHEMA, NAMES), EXPANDO_TO_PARAM);
90 final String notNullJql = StringUtils.defaultString(jql);
91 if (notNullJql.length() > MAX_JQL_LENGTH_FOR_HTTP_GET) {
92 return searchJqlImplPost(maxResults, startAt, expandosValues, notNullJql, fields);
93 } else {
94 return searchJqlImplGet(maxResults, startAt, expandosValues, notNullJql, fields);
95 }
96 }
97
98 private Promise<SearchResult> searchJqlImplGet(@Nullable Integer maxResults, @Nullable Integer startAt, Iterable<String> expandosValues, String jql, @Nullable Set<String> fields) {
99 final UriBuilder uriBuilder = UriBuilder.fromUri(searchUri)
100 .queryParam(JQL_ATTRIBUTE, jql)
101 .queryParam(EXPAND_ATTRIBUTE, Joiner.on(",").join(expandosValues));
102
103 if (fields != null) {
104 uriBuilder.queryParam(FIELDS_ATTRIBUTE, Joiner.on(",").join(fields));
105 }
106 addOptionalQueryParam(uriBuilder, MAX_RESULTS_ATTRIBUTE, maxResults);
107 addOptionalQueryParam(uriBuilder, START_AT_ATTRIBUTE, startAt);
108
109 return getAndParse(uriBuilder.build(), searchResultJsonParser);
110 }
111
112 private void addOptionalQueryParam(final UriBuilder uriBuilder, final String key, final Object... values) {
113 if (values != null && values.length > 0 && values[0] != null) {
114 uriBuilder.queryParam(key, values);
115 }
116 }
117
118 private Promise<SearchResult> searchJqlImplPost(@Nullable Integer maxResults, @Nullable Integer startAt, Iterable<String> expandosValues, String jql, @Nullable Set<String> fields) {
119 final JSONObject postEntity = new JSONObject();
120
121 try {
122 postEntity.put(JQL_ATTRIBUTE, jql)
123 .put(EXPAND_ATTRIBUTE, ImmutableList.copyOf(expandosValues))
124 .putOpt(START_AT_ATTRIBUTE, startAt)
125 .putOpt(MAX_RESULTS_ATTRIBUTE, maxResults);
126
127 if (fields != null) {
128 postEntity.put(FIELDS_ATTRIBUTE, fields);
129 }
130 } catch (JSONException e) {
131 throw new RestClientException(e);
132 }
133 return postAndParse(searchUri, postEntity, searchResultJsonParser);
134 }
135
136 @Override
137 public Promise<Iterable<Filter>> getFavouriteFilters() {
138 return getAndParse(favouriteUri, filtersParser);
139 }
140
141 @Override
142 public Promise<Filter> getFilter(URI filterUri) {
143 return getAndParse(filterUri, filterJsonParser);
144 }
145
146 @Override
147 public Promise<Filter> getFilter(long id) {
148 return getFilter(UriBuilder.fromUri(baseUri).path(String.format(FILTER_PATH_FORMAT, id)).build());
149 }
150 }