1 package com.atlassian.marketplace.client.api;
2
3 import com.atlassian.fugue.Option;
4
5 import com.google.common.collect.ImmutableList;
6
7 import static com.atlassian.fugue.Option.none;
8 import static com.atlassian.marketplace.client.api.QueryProperties.describeOptBoolean;
9 import static com.atlassian.marketplace.client.api.QueryProperties.describeOptEnum;
10 import static com.atlassian.marketplace.client.api.QueryProperties.describeParams;
11 import static com.atlassian.marketplace.client.api.QueryProperties.describeValues;
12 import static com.google.common.base.Preconditions.checkNotNull;
13
14 /**
15 * Encapsulates search parameters that can be passed to {@link Addons} to determine what
16 * subset of add-on listings you are interested in and/or what information should be included
17 * in the results.
18 * @since 2.0.0
19 */
20 public final class AddonQuery implements QueryProperties.AccessToken,
21 QueryProperties.ApplicationCriteria,
22 QueryProperties.Bounds,
23 QueryProperties.Cost,
24 QueryProperties.Hosting,
25 QueryProperties.WithVersion
26 {
27 private static final AddonQuery DEFAULT_QUERY = builder().build();
28
29 private final Option<String> accessToken;
30 private final QueryBuilderProperties.ApplicationCriteriaHelper app;
31 private final QueryBounds bounds;
32 private final Iterable<String> categoryNames;
33 private final Option<Cost> cost;
34 private final boolean forThisUserOnly;
35 private final Option<HostingType> hosting;
36 private final Option<IncludeHiddenType> includeHidden;
37 private final boolean includePrivate;
38 private final Option<String> label;
39 private final Option<TreatPartlyFreeAs> treatPartlyFreeAs;
40 private final Option<String> searchText;
41 private final Option<View> view;
42 private final boolean withVersion;
43
44 /**
45 * Returns a new {@link Builder} for constructing an AddonQuery.
46 */
47 public static Builder builder()
48 {
49 return new Builder();
50 }
51
52 /**
53 * Returns an {@link AddonQuery} with no criteria, which will match any available add-on.
54 */
55 public static AddonQuery any()
56 {
57 return DEFAULT_QUERY;
58 }
59
60 /**
61 * Returns a new {@link Builder} for constructing an {@link AddonQuery} based on an existing {@link AddonQuery}.
62 */
63 public static Builder builder(AddonQuery query)
64 {
65 Builder builder = builder()
66 .application(query.getApplication())
67 .appBuildNumber(query.getAppBuildNumber())
68 .categoryNames(query.getCategoryNames())
69 .cost(query.getCost())
70 .forThisUserOnly(query.isForThisUserOnly())
71 .hosting(query.getHosting())
72 .includeHidden(query.getIncludeHidden())
73 .includePrivate(query.isIncludePrivate())
74 .label(query.getLabel())
75 .treatPartlyFreeAs(query.getTreatPartlyFreeAs())
76 .view(query.getView())
77 .withVersion(query.isWithVersion())
78 .bounds(query.getBounds())
79 .searchText(query.getSearchText());
80
81 return builder;
82 }
83
84 private AddonQuery(Builder builder)
85 {
86 accessToken = builder.accessToken;
87 app = builder.app;
88 bounds = builder.bounds;
89 categoryNames = builder.categoryNames;
90 cost = builder.cost;
91 forThisUserOnly = builder.forThisUserOnly;
92 hosting = builder.hosting;
93 includeHidden = builder.includeHidden;
94 includePrivate = builder.includePrivate;
95 label = builder.label;
96 searchText = builder.searchText;
97 treatPartlyFreeAs = builder.treatPartlyFreeAs;
98 view = builder.view;
99 withVersion = builder.withVersion;
100 }
101
102 @Override
103 public Option<String> getAccessToken()
104 {
105 return accessToken;
106 }
107
108 @Override
109 public Option<ApplicationKey> getApplication()
110 {
111 return app.application;
112 }
113
114 @Override
115 public Option<Integer> getAppBuildNumber()
116 {
117 return app.appBuildNumber;
118 }
119
120 /**
121 * The list of add-on category names, if any, that the client has specified to restrict
122 * the query results.
123 * @see Builder#categoryNames(Iterable)
124 */
125 public Iterable<String> getCategoryNames()
126 {
127 return categoryNames;
128 }
129
130 @Override
131 public Option<Cost> getCost()
132 {
133 return cost;
134 }
135
136 /**
137 * True if the client is querying only add-ons that are associated with the current
138 * authenticated user's vendor(s).
139 * @see Builder#forThisUserOnly(boolean)
140 */
141 public boolean isForThisUserOnly()
142 {
143 return forThisUserOnly;
144 }
145
146 /**
147 * The subset of normally hidden add-ons, if any, that the client has specified to include
148 * in the query results.
149 * @see Builder#includeHidden(Option)
150 */
151 public Option<IncludeHiddenType> getIncludeHidden()
152 {
153 return includeHidden;
154 }
155
156 /**
157 * True if the client is querying private add-ons as well as public add-ons.
158 * @see Builder#includePrivate(boolean)
159 */
160 public boolean isIncludePrivate()
161 {
162 return includePrivate;
163 }
164
165 @Override
166 public Option<HostingType> getHosting()
167 {
168 return hosting;
169 }
170
171 /**
172 * The marketing label string, if any, that the client has specified to restrict the query results.
173 * @see Builder#label(Option)
174 */
175 public Option<String> getLabel()
176 {
177 return label;
178 }
179
180 /**
181 * The {@link TreatPartlyFreeAs} value, if any, that the client has specified to determine how
182 * free-tier listings will be treated in the query.
183 * @see Builder#treatPartlyFreeAs(Option)
184 */
185 public Option<TreatPartlyFreeAs> getTreatPartlyFreeAs()
186 {
187 return treatPartlyFreeAs;
188 }
189
190 /**
191 * The search text, if any, that the client has specified for the query.
192 * @see Builder#searchText(Option)
193 */
194 public Option<String> getSearchText()
195 {
196 return searchText;
197 }
198
199 /**
200 * The {@link View} value, if any, that the client has specified as a filter and/or sort order
201 * for the query.
202 * @see Builder#view(Option)
203 */
204 public Option<View> getView()
205 {
206 return view;
207 }
208
209 @Override
210 public boolean isWithVersion()
211 {
212 return withVersion;
213 }
214
215 @Override
216 public QueryBounds getBounds()
217 {
218 return bounds;
219 }
220
221 @SuppressWarnings("unchecked")
222 @Override
223 public String toString()
224 {
225 return describeParams("AddonQuery",
226 describeValues("accessToken", accessToken),
227 app.describe(),
228 describeValues("categoryNames", categoryNames),
229 describeOptEnum("cost", cost),
230 describeOptBoolean("forThisUserOnly", forThisUserOnly),
231 describeOptEnum("hosting", hosting),
232 describeOptEnum("includeHidden", includeHidden),
233 describeOptBoolean("includePrivate", includePrivate),
234 describeValues("label", label),
235 describeValues("searchText", searchText),
236 describeOptEnum("treatPartlyFreeAs", treatPartlyFreeAs),
237 describeOptEnum("view", view),
238 describeOptBoolean("withVersion", withVersion),
239 bounds.describe()
240 );
241 }
242
243 @Override
244 public boolean equals(Object other)
245 {
246 return (other instanceof AddonQuery) ? toString().equals(other.toString()) : false;
247 }
248
249 @Override
250 public int hashCode()
251 {
252 return toString().hashCode();
253 }
254
255 /**
256 * Builder class for {@link AddonQuery}. Use {@link AddonQuery#builder()} to create an instance.
257 */
258 public static class Builder implements QueryBuilderProperties.AccessToken<Builder>,
259 QueryBuilderProperties.ApplicationCriteria<Builder>,
260 QueryBuilderProperties.Bounds<Builder>,
261 QueryBuilderProperties.Cost<Builder>,
262 QueryBuilderProperties.Hosting<Builder>,
263 QueryBuilderProperties.WithVersion<Builder>
264 {
265 private Option<String> accessToken = none();
266 private QueryBuilderProperties.ApplicationCriteriaHelper app = new QueryBuilderProperties.ApplicationCriteriaHelper();
267 private QueryBounds bounds = QueryBounds.defaultBounds();
268 private Iterable<String> categoryNames = ImmutableList.of();
269 private Option<Cost> cost = none();
270 private boolean forThisUserOnly = false;
271 private Option<HostingType> hosting = none();
272 private Option<IncludeHiddenType> includeHidden = none();
273 private boolean includePrivate = false;
274 private Option<String> label = none();
275 private Option<TreatPartlyFreeAs> treatPartlyFreeAs = none();
276 private Option<String> searchText = none();
277 private Option<View> view = none();
278 private boolean withVersion = false;
279
280 /**
281 * Returns an immutable {@link AddonQuery} based on the current builder properties.
282 */
283 public AddonQuery build()
284 {
285 return new AddonQuery(this);
286 }
287
288 @Override
289 public Builder accessToken(Option<String> accessToken)
290 {
291 this.accessToken = checkNotNull(accessToken);
292 return this;
293 }
294
295 @Override
296 public Builder application(Option<ApplicationKey> application)
297 {
298 app = app.application(application);
299 return this;
300 }
301
302 @Override
303 public Builder appBuildNumber(Option<Integer> appBuildNumber)
304 {
305 app = app.appBuildNumber(appBuildNumber);
306 return this;
307 }
308
309 /**
310 * Restricts the query to add-ons that belong to one of the specified categories. An empty list
311 * means there is no such restriction. To obtain a list of allowable category names, use
312 * {@link AddonCategories}.
313 * @param categoryNames category names to search for, or an empty {@code Iterable} if you do
314 * not want to filter by add-on category
315 * @return the same query builder
316 * @see AddonQuery#getCategoryNames()
317 */
318 public Builder categoryNames(Iterable<String> categoryNames)
319 {
320 this.categoryNames = ImmutableList.copyOf(categoryNames);
321 return this;
322 }
323
324 @Override
325 public Builder cost(Option<Cost> cost)
326 {
327 this.cost = checkNotNull(cost);
328 return this;
329 }
330
331 /**
332 * Specifies whether to query only the addons associated with the current authenticated user's vendor(s).
333 * @param forThisUserOnly true to query only addons associated with the current
334 * authenticated user; false (the default) to query add-ons from any vendor.
335 * @return the same query builder
336 * @see AddonQuery#isForThisUserOnly()
337 */
338 public Builder forThisUserOnly(boolean forThisUserOnly)
339 {
340 this.forThisUserOnly = forThisUserOnly;
341 return this;
342 }
343
344 @Override
345 public Builder hosting(Option<HostingType> hosting)
346 {
347 this.hosting = checkNotNull(hosting);
348 return this;
349 }
350
351 /**
352 * Specifies whether to include special "hidden" add-ons in the results. These are not private
353 * listings; they are add-ons that Atlassian has configured to be invisible on the Marketplace site, but
354 * still discoverable from within applications under some circumstances (such as language packs).
355 * @param includeHidden the type of hidden add-ons to include, or {@link Option#none()} (the default)
356 * to exclude them all
357 * @return the same query builder
358 * @see AddonQuery#getIncludeHidden()
359 */
360 public Builder includeHidden(Option<IncludeHiddenType> includeHidden)
361 {
362 this.includeHidden = checkNotNull(includeHidden);
363 return this;
364 }
365
366 /**
367 * Specifies whether to include private add-ons in the results. This will only have an effect if you
368 * are authenticated, and you will only be able to see private add-ons that you would be able to access
369 * in the Marketplace site (that is, unless you are a Marketplace administrator, only private add-ons
370 * from your vendor(s) will be accessible).
371 * @param includePrivate true to include private add-ons; false (the default) to include only public
372 * add-ons
373 * @return the same query builder
374 * @see AddonQuery#isIncludePrivate()
375 */
376 public Builder includePrivate(boolean includePrivate)
377 {
378 this.includePrivate = includePrivate;
379 return this;
380 }
381
382 /**
383 * Restricts the query to add-ons that have been tagged with the specified label string.
384 * This is an internal identifier, not visible on the Marketplace site, that Atlassian
385 * administrators may use to create subsets of add-ons for any purpose.
386 * @param label label string to search for, or {@link Option#none()} for no label search
387 * @return the same query builder
388 * @see AddonQuery#getLabel()
389 */
390 public Builder label(Option<String> label)
391 {
392 this.label = checkNotNull(label);
393 return this;
394 }
395
396 /**
397 * Specify how partly free, paid via Atlassian add-ons (those with a free tier) should be treated.
398 * By default none is included.
399 *
400 * @param treatPartlyFreeAs how to treat partly free, PvA add-ons in queries
401 * @return the same query builder
402 */
403 public Builder treatPartlyFreeAs(Option<TreatPartlyFreeAs> treatPartlyFreeAs)
404 {
405 this.treatPartlyFreeAs = treatPartlyFreeAs;
406 return this;
407 }
408
409 /**
410 * Restricts the query to add-ons that have the specified text somewhere in their name or
411 * description.
412 * @param searchText text to search for, or {@link Option#none()} for no text search
413 * @return the same query builder
414 * @see AddonQuery#getSearchText()
415 */
416 public Builder searchText(Option<String> searchText)
417 {
418 this.searchText = checkNotNull(searchText);
419 return this;
420 }
421
422 /**
423 * Specifies an {@link View} which determines the overall sort order and/or subset of
424 * add-ons to be queried.
425 * @param view an {@link View} value, or {@link Option#none()} for the default view
426 * @return the same query builder
427 * @see AddonQuery#getView()
428 */
429 public Builder view(Option<View> view)
430 {
431 this.view = checkNotNull(view);
432 return this;
433 }
434
435 @Override
436 public Builder withVersion(boolean withVersion)
437 {
438 this.withVersion = withVersion;
439 return this;
440 }
441
442 @Override
443 public Builder bounds(QueryBounds bounds)
444 {
445 this.bounds = checkNotNull(bounds);
446 return this;
447 }
448 }
449
450 /**
451 * Constants representing subsets of add-ons that are normally hidden but may still be queried.
452 * @see AddonQuery.Builder#includeHidden(Option)
453 */
454 public enum IncludeHiddenType implements EnumWithKey
455 {
456 /**
457 * Add-ons that do not appear on the Marketplace site, but that are normally discoverable from within
458 * an application (such as language packs), will be included in the query results.
459 */
460 VISIBLE_IN_APP ("visibleInApp"),
461
462 /**
463 * Add-ons that do not appear on the Marketplace site will be included in the query results,
464 * even if they are not normally discoverable from within an application (for instance, add-ons
465 * that are bundled within an application but may have updates made available via Marketplace).
466 */
467 ALL ("all");
468
469 private final String key;
470
471 private IncludeHiddenType(String key)
472 {
473 this.key = key;
474 }
475
476 @Override
477 public String getKey()
478 {
479 return key;
480 }
481 }
482
483 /**
484 * Determines how paid-via-Atlassian add-ons with free tiers, such as those where the vendor has opted into the
485 * Cloud free-for-five tier, are treated in queries.
486 * @see AddonQuery.Builder#treatPartlyFreeAs(Option)
487 */
488 public enum TreatPartlyFreeAs implements EnumWithKey
489 {
490 /**
491 * Add-ons with free tiers will be effectively treated as free in queries. An example of when this should be
492 * used is when the application has a Cloud free for X number of users and there are less than or equal to
493 * X active users.
494 */
495 FREE("free"),
496
497 /**
498 * Add-ons with free tiers will be effectively treated as paid in queries.
499 */
500 PAID("paid");
501
502 private final String key;
503
504 private TreatPartlyFreeAs(String key)
505 {
506 this.key = key;
507 }
508
509 @Override
510 public String getKey()
511 {
512 return key;
513 }
514 }
515
516 /**
517 * Constants representing preset add-on list views, which may affect both the set of
518 * add-ons being queried and the sort order of the results.
519 * @see AddonQuery.Builder#view(Option)
520 */
521 public enum View implements EnumWithKey
522 {
523 /**
524 * Restricts the query to add-ons made by Atlassian, with the most recently updated add-ons first.
525 */
526 BY_ATLASSIAN ("atlassian"),
527 /**
528 * Restricts the query to a set of add-ons picked by Atlassian staff, with the most recently
529 * updated add-ons first.
530 */
531 FEATURED ("featured"),
532 /**
533 * Query all add-ons, sorted by rating.
534 */
535 HIGHEST_RATED ("highest-rated"),
536 /**
537 * Queries all add-ons, in descending order of total number of downloads.
538 */
539 POPULAR ("popular"),
540 /**
541 * Queries all add-ons, in descending order of modification date.
542 */
543 RECENTLY_UPDATED ("recent"),
544 /**
545 * Queries all Paid-via-Atlassian add-ons, in descending order of gross sales.
546 */
547 TOP_GROSSING ("top-grossing"),
548 /**
549 * Queries all add-ons, in descending order of number of recent downloads.
550 */
551 TRENDING ("trending");
552
553 private final String key;
554
555 private View(String key)
556 {
557 this.key = key;
558 }
559
560 @Override
561 public String getKey()
562 {
563 return key;
564 }
565 }
566 }