001package com.box.sdk;
002
003import static java.util.stream.Collectors.toList;
004
005import com.eclipsesource.json.Json;
006import com.eclipsesource.json.JsonArray;
007import com.eclipsesource.json.JsonObject;
008import com.eclipsesource.json.JsonValue;
009import java.util.ArrayList;
010import java.util.Arrays;
011import java.util.List;
012
013/**
014 * Represents Metadata Query.
015 */
016public class MetadataQuery {
017    static final String FROM = "from";
018    static final String LIMIT = "limit";
019    static final String QUERY = "query";
020    static final String ANCESTOR_FOLDER_ID = "ancestor_folder_id";
021    static final String MARKER = "marker";
022    static final String ORDER_BY = "order_by";
023    static final String FIELDS = "fields";
024    static final String QUERY_PARAMS = "query_params";
025    private final String from;
026    private final int limit;
027    private String query;
028    private JsonObject queryParameters = new JsonObject();
029    private String ancestorFolderId = "0";
030    private List<OrderBy> orderBy = new ArrayList<>();
031    private String marker;
032    private List<String> fields = new ArrayList<>();
033
034    /**
035     * Creates Metadata Query
036     *
037     * @param from  The template used in the query. Must be in the form scope_enterpriseID.templateKey
038     * @param limit Max results to return for a single request (0-100 inclusive)
039     */
040    public MetadataQuery(String from, int limit) {
041        this.from = from;
042        this.limit = limit;
043    }
044
045    /**
046     * Creates Metadata Query
047     *
048     * @param from The template used in the query. Must be in the form scope.templateKey
049     */
050    public MetadataQuery(String from) {
051        this(from, 100);
052    }
053
054    /**
055     * The logical expression of the query
056     *
057     * @param query Query string
058     * @return Returns current MetadataQuery object
059     */
060    public MetadataQuery setQuery(String query) {
061        this.query = query;
062        return this;
063    }
064
065    /**
066     * Sets the folder_id to which to restrain the query.
067     * If not set query starts at root level.
068     *
069     * @param ancestorFolderId The folder id
070     * @return Returns current MetadataQuery object
071     */
072    public MetadataQuery setAncestorFolderId(String ancestorFolderId) {
073        this.ancestorFolderId = ancestorFolderId;
074        return this;
075    }
076
077    /**
078     * The marker to use for requesting the next page
079     *
080     * @param marker Marker string.
081     * @return Returns current MetadataQuery object
082     */
083    public MetadataQuery setMarker(String marker) {
084        this.marker = marker;
085        return this;
086    }
087
088    /**
089     * The field_key(s) to order on and the corresponding direction(s)
090     *
091     * @param fields Fields with sort order
092     * @return Returns current MetadataQuery object
093     */
094    public MetadataQuery setOrderBy(OrderBy... fields) {
095        this.orderBy = new ArrayList<>();
096        this.orderBy.addAll(Arrays.asList(fields));
097        return this;
098    }
099
100    MetadataQuery setOrderBy(JsonArray orderBy) {
101        if (orderBy != null) {
102            this.orderBy = orderBy.values().stream().map(OrderBy::fromJson).collect(toList());
103        }
104        return this;
105    }
106
107    /**
108     * The fields to retrieve.
109     *
110     * @param fields Field names
111     * @return Returns current MetadataQuery object
112     */
113    public MetadataQuery setFields(String... fields) {
114        this.fields = new ArrayList<>();
115        this.fields.addAll(Arrays.asList(fields));
116        return this;
117    }
118
119    /**
120     * Adds parameter to query
121     *
122     * @param name  Parameter name
123     * @param value Parameter value
124     * @return Returns current MetadataQuery object
125     */
126    public MetadataQuery addParameter(String name, String value) {
127        this.queryParameters.add(name, value);
128        return this;
129    }
130
131    /**
132     * Adds parameter to query
133     *
134     * @param name  Parameter name
135     * @param value Parameter value
136     * @return Returns current MetadataQuery object
137     */
138    public MetadataQuery addParameter(String name, int value) {
139        this.queryParameters.add(name, value);
140        return this;
141    }
142
143    /**
144     * Adds parameter to query
145     *
146     * @param name  Parameter name
147     * @param value Parameter value
148     * @return Returns current MetadataQuery object
149     */
150    public MetadataQuery addParameter(String name, boolean value) {
151        this.queryParameters.add(name, value);
152        return this;
153    }
154
155    /**
156     * Adds parameter to query
157     *
158     * @param name  Parameter name
159     * @param value Parameter value
160     * @return Returns current MetadataQuery object
161     */
162    public MetadataQuery addParameter(String name, float value) {
163        this.queryParameters.add(name, value);
164        return this;
165    }
166
167    /**
168     * Adds parameter to query
169     *
170     * @param name  Parameter name
171     * @param value Parameter value
172     * @return Returns current MetadataQuery object
173     */
174    public MetadataQuery addParameter(String name, long value) {
175        this.queryParameters.add(name, value);
176        return this;
177    }
178
179    /**
180     * Adds parameter to query
181     *
182     * @param name  Parameter name
183     * @param value Parameter value
184     * @return Returns current MetadataQuery object
185     */
186    public MetadataQuery addParameter(String name, double value) {
187        this.queryParameters.add(name, value);
188        return this;
189    }
190
191    /**
192     * Adds parameter to query
193     *
194     * @param name  Parameter name
195     * @param value Parameter value
196     * @return Returns current MetadataQuery object
197     */
198    public MetadataQuery addParameter(String name, JsonValue value) {
199        this.queryParameters.add(name, Json.parse(value.toString()));
200        return this;
201    }
202
203    MetadataQuery setQueryParams(JsonObject queryParameters) {
204        this.queryParameters = new JsonObject(queryParameters);
205        return this;
206    }
207
208    JsonObject toJsonObject() {
209        JsonObject jsonObject = new JsonObject()
210            .add(FROM, from)
211            .add(LIMIT, limit);
212        if (query != null) {
213            jsonObject.add(QUERY, query);
214        }
215        if (ancestorFolderId != null) {
216            jsonObject.add(ANCESTOR_FOLDER_ID, ancestorFolderId);
217        }
218        if (marker != null) {
219            jsonObject.add(MARKER, marker);
220        }
221        if (!orderBy.isEmpty()) {
222            JsonArray orderByJson = new JsonArray();
223            orderBy.stream().map(OrderBy::toJsonObject).forEach(orderByJson::add);
224            jsonObject.add(ORDER_BY, orderByJson);
225        }
226        if (!fields.isEmpty()) {
227            JsonArray fieldsJson = new JsonArray();
228            fields.forEach(fieldsJson::add);
229            jsonObject.add(FIELDS, fieldsJson);
230        }
231        if (queryParameters.iterator().hasNext()) {
232            jsonObject.add(QUERY_PARAMS, new JsonObject(queryParameters));
233        }
234        return jsonObject;
235    }
236
237    int getLimit() {
238        return limit;
239    }
240
241    String getMarker() {
242        return marker;
243    }
244
245    public static final class OrderBy {
246
247        static final String FIELD_KEY = "field_key";
248        static final String DIRECTION = "direction";
249        static final String DIRECTION_ASCENDING = "asc";
250        static final String DIRECTION_DESCENDING = "desc";
251        private final String fieldName;
252        private final String direction;
253
254        private OrderBy(String fieldName, String direction) {
255            this.fieldName = fieldName;
256            this.direction = direction;
257        }
258
259        JsonObject toJsonObject() {
260            return new JsonObject().add(FIELD_KEY, fieldName).add(DIRECTION, direction);
261        }
262
263        /**
264         * Creates OrderBy for ascending sort with a specified field.
265         * @param fieldName Name of a field
266         * @return OrderBy instance
267         */
268        public static OrderBy ascending(String fieldName) {
269            return new OrderBy(fieldName, DIRECTION_ASCENDING);
270        }
271
272        /**
273         * Creates OrderBy for descending sort with a specified field.
274         * @param fieldName Name of a field
275         * @return OrderBy instance
276         */
277        public static OrderBy descending(String fieldName) {
278            return new OrderBy(fieldName, DIRECTION_DESCENDING);
279        }
280
281        static OrderBy fromJson(JsonValue jsonValue) {
282            if (jsonValue.isObject()) {
283                JsonObject object = jsonValue.asObject();
284                String fieldName = object.get(FIELD_KEY).asString();
285                String direction = object.get(DIRECTION).asString().toLowerCase();
286                if (!DIRECTION_ASCENDING.equals(direction) && !DIRECTION_DESCENDING.equals(direction)) {
287                    throw new RuntimeException(
288                        String.format("Unsupported sort direction [%s] for field [%s]", direction, fieldName)
289                    );
290                }
291                return object.getString(DIRECTION, "").equals(DIRECTION_ASCENDING)
292                    ? ascending(fieldName)
293                    : descending(fieldName);
294            }
295            throw new RuntimeException("Unsupported json " + jsonValue);
296        }
297    }
298}