001package com.box.sdk;
002
003import java.net.URL;
004import java.util.ArrayList;
005import java.util.List;
006
007import com.eclipsesource.json.JsonArray;
008import com.eclipsesource.json.JsonObject;
009import com.eclipsesource.json.JsonValue;
010
011/**
012 * The MetadataTemplate class represents the Box metadata template object.
013 * Templates allow the metadata service to provide a multitude of services,
014 * such as pre-defining sets of key:value pairs or schema enforcement on specific fields.
015 *
016 * @see <a href="https://developer.box.com/reference/resources/metadata-templates/">Box metadata templates</a>
017 */
018public class MetadataTemplate extends BoxJSONObject {
019
020    /**
021     * @see #getMetadataTemplate(BoxAPIConnection)
022     */
023    public static final URLTemplate METADATA_TEMPLATE_URL_TEMPLATE
024        = new URLTemplate("metadata_templates/%s/%s/schema");
025
026    /**
027     * @see #getMetadataTemplateByID(BoxAPIConnection, String)
028     */
029    public static final URLTemplate METADATA_TEMPLATE_BY_ID_URL_TEMPLATE = new URLTemplate("metadata_templates/%s");
030
031    /**
032     * @see #createMetadataTemplate(BoxAPIConnection, String, String, String, boolean, List)
033     */
034    public static final URLTemplate METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE
035        = new URLTemplate("metadata_templates/schema");
036
037    /**
038     * @see #getEnterpriseMetadataTemplates(String, int, BoxAPIConnection, String...)
039     */
040    public static final URLTemplate ENTERPRISE_METADATA_URL_TEMPLATE = new URLTemplate("metadata_templates/%s");
041
042    /**
043     *
044     */
045    private static final URLTemplate METADATA_QUERIES_URL_TEMPLATE = new URLTemplate("metadata_queries/execute_read");
046
047    /**
048     * Default metadata type to be used in query.
049     */
050    private static final String DEFAULT_METADATA_TYPE = "properties";
051
052    /**
053     * Global metadata scope. Used by default if the metadata type is "properties".
054     */
055    private static final String GLOBAL_METADATA_SCOPE = "global";
056
057    /**
058     * Enterprise metadata scope. Used by default if the metadata type is not "properties".
059     */
060    private static final String ENTERPRISE_METADATA_SCOPE = "enterprise";
061
062    /**
063     * Default number of entries per page.
064     */
065    private static final int DEFAULT_ENTRIES_LIMIT = 100;
066
067    /**
068     * @see #getID()
069     */
070    private String id;
071
072    /**
073     * @see #getTemplateKey()
074     */
075    private String templateKey;
076
077    /**
078     * @see #getScope()
079     */
080    private String scope;
081
082    /**
083     * @see #getDisplayName()
084     */
085    private String displayName;
086
087    /**
088     * @see #getIsHidden()
089     */
090    private Boolean isHidden;
091
092    /**
093     * @see #getFields()
094     */
095    private List<Field> fields;
096
097    /**
098     * @see #getCopyInstanceOnItemCopy()
099     */
100    private Boolean copyInstanceOnItemCopy;
101
102    /**
103     * Constructs an empty metadata template.
104     */
105    public MetadataTemplate() {
106        super();
107    }
108
109    /**
110     * Constructs a metadata template from a JSON string.
111     * @param json the json encoded metadate template.
112     */
113    public MetadataTemplate(String json) {
114        super(json);
115    }
116
117    /**
118     * Constructs a metadate template from a JSON object.
119     * @param jsonObject the json encoded metadate template.
120     */
121    MetadataTemplate(JsonObject jsonObject) {
122        super(jsonObject);
123    }
124
125    /**
126     * Gets the ID of the template.
127     * @return the template ID.
128     */
129    public String getID() {
130        return this.id;
131    }
132
133    /**
134     * Gets the unique template key to identify the metadata template.
135     * @return the unique template key to identify the metadata template.
136     */
137    public String getTemplateKey() {
138        return this.templateKey;
139    }
140
141    /**
142     * Gets the metadata template scope.
143     * @return the metadata template scope.
144     */
145    public String getScope() {
146        return this.scope;
147    }
148
149    /**
150     * Gets the displayed metadata template name.
151     * @return the displayed metadata template name.
152     */
153    public String getDisplayName() {
154        return this.displayName;
155    }
156
157    /**
158     * Gets is the metadata template hidden.
159     * @return is the metadata template hidden.
160     */
161    public Boolean getIsHidden() {
162        return this.isHidden;
163    }
164
165    /**
166     * Gets the iterable with all fields the metadata template contains.
167     * @return the iterable with all fields the metadata template contains.
168     */
169    public List<Field> getFields() {
170        return this.fields;
171    }
172
173    /**
174     * Gets whether the copy operation should copy the metadata along with the item.
175     * @return whether the copy operation should copy the metadata along with the item.
176     */
177    public Boolean getCopyInstanceOnItemCopy() {
178        return this.copyInstanceOnItemCopy;
179    }
180
181    /**
182     * {@inheritDoc}
183     */
184    @Override
185    void parseJSONMember(JsonObject.Member member) {
186        JsonValue value = member.getValue();
187        String memberName = member.getName();
188        if (memberName.equals("templateKey")) {
189            this.templateKey = value.asString();
190        } else if (memberName.equals("scope")) {
191            this.scope = value.asString();
192        } else if (memberName.equals("displayName")) {
193            this.displayName = value.asString();
194        } else if (memberName.equals("hidden")) {
195            this.isHidden = value.asBoolean();
196        } else if (memberName.equals("fields")) {
197            this.fields = new ArrayList<Field>();
198            for (JsonValue field: value.asArray()) {
199                this.fields.add(new Field(field.asObject()));
200            }
201        } else if (memberName.equals("id")) {
202            this.id = value.asString();
203        } else if (memberName.equals("copyInstanceOnItemCopy")) {
204            this.copyInstanceOnItemCopy = value.asBoolean();
205        }
206    }
207
208    /**
209     * Creates new metadata template.
210     * @param api the API connection to be used.
211     * @param scope the scope of the object.
212     * @param templateKey a unique identifier for the template.
213     * @param displayName the display name of the field.
214     * @param hidden whether this template is hidden in the UI.
215     * @param fields the ordered set of fields for the template
216     * @return the metadata template returned from the server.
217     */
218    public static MetadataTemplate createMetadataTemplate(BoxAPIConnection api, String scope, String templateKey,
219            String displayName, boolean hidden, List<Field> fields) {
220        return createMetadataTemplate(api, scope, templateKey, displayName, hidden, fields, null);
221    }
222
223    /**
224     * Creates new metadata template.
225     * @param api the API connection to be used.
226     * @param scope the scope of the object.
227     * @param templateKey a unique identifier for the template.
228     * @param displayName the display name of the field.
229     * @param hidden whether this template is hidden in the UI.
230     * @param fields the ordered set of fields for the template
231     * @param copyInstanceOnItemCopy determines whether the copy operation should copy the metadata along with the item.
232     * @return the metadata template returned from the server.
233     */
234    public static MetadataTemplate createMetadataTemplate(BoxAPIConnection api, String scope, String templateKey,
235            String displayName, Boolean hidden, List<Field> fields, Boolean copyInstanceOnItemCopy) {
236
237        JsonObject jsonObject = new JsonObject();
238        jsonObject.add("scope", scope);
239        jsonObject.add("displayName", displayName);
240
241        if (hidden != null) {
242            jsonObject.add("hidden", hidden);
243        }
244
245        if (copyInstanceOnItemCopy != null) {
246            jsonObject.add("copyInstanceOnItemCopy", copyInstanceOnItemCopy);
247        }
248
249        if (templateKey != null) {
250            jsonObject.add("templateKey", templateKey);
251        }
252
253        JsonArray fieldsArray = new JsonArray();
254        if (fields != null && !fields.isEmpty()) {
255            for (Field field : fields) {
256                JsonObject fieldObj = getFieldJsonObject(field);
257
258                fieldsArray.add(fieldObj);
259            }
260
261            jsonObject.add("fields", fieldsArray);
262        }
263
264        URL url = METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE.build(api.getBaseURL());
265        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
266        request.setBody(jsonObject.toString());
267
268        BoxJSONResponse response = (BoxJSONResponse) request.send();
269        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
270
271        return new MetadataTemplate(responseJSON);
272    }
273
274    /**
275     * Gets the JsonObject representation of the given field object.
276     * @param field represents a template field
277     * @return the json object
278     */
279    private static JsonObject getFieldJsonObject(Field field) {
280        JsonObject fieldObj = new JsonObject();
281        fieldObj.add("type", field.getType());
282        fieldObj.add("key", field.getKey());
283        fieldObj.add("displayName", field.getDisplayName());
284
285        String fieldDesc = field.getDescription();
286        if (fieldDesc != null) {
287            fieldObj.add("description", field.getDescription());
288        }
289
290        Boolean fieldIsHidden = field.getIsHidden();
291        if (fieldIsHidden != null) {
292            fieldObj.add("hidden", field.getIsHidden());
293        }
294
295        JsonArray array = new JsonArray();
296        List<String> options = field.getOptions();
297        if (options != null && !options.isEmpty()) {
298            for (String option : options) {
299                JsonObject optionObj = new JsonObject();
300                optionObj.add("key", option);
301
302                array.add(optionObj);
303            }
304            fieldObj.add("options", array);
305        }
306
307        return fieldObj;
308    }
309
310    /**
311     * Updates the schema of an existing metadata template.
312     *
313     * @param api the API connection to be used
314     * @param scope the scope of the object
315     * @param template Unique identifier of the template
316     * @param fieldOperations the fields that needs to be updated / added in the template
317     * @return the updated metadata template
318     */
319    public static MetadataTemplate updateMetadataTemplate(BoxAPIConnection api, String scope, String template,
320            List<FieldOperation> fieldOperations) {
321
322        JsonArray array = new JsonArray();
323
324        for (FieldOperation fieldOperation : fieldOperations) {
325            JsonObject jsonObject = getFieldOperationJsonObject(fieldOperation);
326            array.add(jsonObject);
327        }
328
329        QueryStringBuilder builder = new QueryStringBuilder();
330        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlpha(api.getBaseURL(), scope, template);
331        BoxJSONRequest request = new BoxJSONRequest(api, url, "PUT");
332        request.setBody(array.toString());
333
334        BoxJSONResponse response = (BoxJSONResponse) request.send();
335        JsonObject responseJson = JsonObject.readFrom(response.getJSON());
336
337        return new MetadataTemplate(responseJson);
338    }
339
340    /**
341     * Deletes the schema of an existing metadata template.
342     *
343     * @param api the API connection to be used
344     * @param scope the scope of the object
345     * @param template Unique identifier of the template
346     */
347    public static void deleteMetadataTemplate(BoxAPIConnection api, String scope, String template) {
348
349        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlpha(api.getBaseURL(), scope, template);
350        BoxJSONRequest request = new BoxJSONRequest(api, url, "DELETE");
351
352        request.send();
353    }
354
355    /**
356     * Executes a metadata query.
357     *
358     * @param api The API connection to be used
359     * @param from The template used in the query. Must be in the form scope.templateKey
360     * @param ancestorFolderId The folder_id to which to restrain the query
361     * @return An iterable of BoxMetadataQueryItem search results
362     * @deprecated use executeMetadataQuery() that returns BoxItem.Info instead.
363     */
364    @Deprecated
365    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
366                                                            String from, String ancestorFolderId) {
367        return executeMetadataQuery(api, from, null, null, ancestorFolderId, null, null, 100, null);
368    }
369
370    /**
371     * Executes a metadata query.
372     *
373     * @param api The API connection to be used
374     * @param from The template used in the query. Must be in the form scope.templateKey
375     * @param ancestorFolderId The folder_id to which to restrain the query
376     * @param fields The fields to retrieve.
377     * @return An iterable of BoxItem.Info search results
378     */
379    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
380                                                            String from, String ancestorFolderId, String ... fields) {
381        return executeMetadataQuery(api, from, null, null, ancestorFolderId, null, null, 100, null, fields);
382    }
383
384    /**
385      * Executes a metadata query.
386      *
387      * @param api The API connection to be used
388      * @param from The template used in the query. Must be in the form scope.templateKey
389      * @param query The logical expression of the query
390      * @param queryParameters Required if query present. The arguments for the query
391      * @param ancestorFolderId The folder_id to which to restrain the query
392      * @return An iterable of BoxMetadataQueryItem search results
393      * @deprecated use executeMetadataQuery() that returns BoxItem.Info instead.
394      */
395    @Deprecated
396    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
397                                                            String from, String query, JsonObject queryParameters,
398                                                            String ancestorFolderId) {
399        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, null, null, 100, null);
400    }
401
402    /**
403      * Executes a metadata query.
404      *
405      * @param api The API connection to be used
406      * @param from The template used in the query. Must be in the form scope.templateKey
407      * @param query The logical expression of the query
408      * @param queryParameters Required if query present. The arguments for the query
409      * @param ancestorFolderId The folder_id to which to restrain the query
410      * @param fields The fields to retrieve.
411      * @return An iterable of BoxItem.Info search results
412      */
413    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
414                                                            String from, String query, JsonObject queryParameters,
415                                                            String ancestorFolderId, String ... fields) {
416        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, null, null, 100, null, fields);
417    }
418
419    /**
420      * Executes a metadata query.
421      *
422      * @param api The API connection to be used
423      * @param from The template used in the query. Must be in the form scope.templateKey
424      * @param query The logical expression of the query
425      * @param queryParameters Required if query present. The arguments for the query
426      * @param ancestorFolderId The folder_id to which to restrain the query
427      * @param indexName The name of the Index to use
428      * @param orderBy The field_key(s) to order on and the corresponding direction(s)
429      * @return An iterable of BoxMetadataQueryItem search results
430      * @deprecated use executeMetadataQuery() that returns BoxItem.Info instead.
431      */
432    @Deprecated
433    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
434                                                            String from, String query, JsonObject queryParameters,
435                                                            String ancestorFolderId, String indexName,
436                                                            JsonArray orderBy) {
437        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, indexName, orderBy, 100, null);
438    }
439
440    /**
441      * Executes a metadata query.
442      *
443      * @param api The API connection to be used
444      * @param from The template used in the query. Must be in the form scope.templateKey
445      * @param query The logical expression of the query
446      * @param queryParameters Required if query present. The arguments for the query
447      * @param ancestorFolderId The folder_id to which to restrain the query
448      * @param indexName The name of the Index to use
449      * @param orderBy The field_key(s) to order on and the corresponding direction(s)
450      * @param fields The fields to retrieve.
451      * @return An iterable of BoxItem.Info search results
452      */
453    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
454                                                            String from, String query, JsonObject queryParameters,
455                                                            String ancestorFolderId, String indexName,
456                                                            JsonArray orderBy, String ... fields) {
457        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, indexName, orderBy, 100,
458                            null, fields);
459    }
460
461    /**
462     * Executes a metadata query.
463     *
464     * @param api The API connection to be used
465     * @param from The template used in the query. Must be in the form scope.templateKey
466     * @param query The logical expression of the query
467     * @param queryParameters Required if query present. The arguments for the query
468     * @param ancestorFolderId The folder_id to which to restrain the query
469     * @param indexName The name of the Index to use
470     * @param orderBy The field_key(s) to order on and the corresponding direction(s)
471     * @param limit Max results to return for a single request (0-100 inclusive)
472     * @param marker The marker to use for requesting the next page
473     * @return An iterable of BoxMetadataQueryItem search results
474     * @deprecated use executeMetadataQuery() that returns BoxItem.Info instead.
475     */
476    @Deprecated
477    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
478                                                            String from, String query, JsonObject queryParameters,
479                                                            String ancestorFolderId, String indexName,
480                                                            JsonArray orderBy, int limit, String marker) {
481        JsonObject jsonObject = createMetadataQueryBody(from, query, queryParameters, ancestorFolderId,
482                                                        indexName, orderBy, limit, marker);
483
484        URL url = METADATA_QUERIES_URL_TEMPLATE.build(api.getBaseURL());
485        return new BoxResourceIterable<BoxMetadataQueryItem>(api, url, limit, jsonObject, marker) {
486
487            @Override
488            protected BoxMetadataQueryItem factory(JsonObject jsonObject) {
489                return new BoxMetadataQueryItem(jsonObject, api);
490            }
491        };
492    }
493
494    /**
495     * Executes a metadata query.
496     *
497     * @param api The API connection to be used
498     * @param from The template used in the query. Must be in the form scope.templateKey
499     * @param query The logical expression of the query
500     * @param queryParameters Required if query present. The arguments for the query
501     * @param ancestorFolderId The folder_id to which to restrain the query
502     * @param indexName The name of the Index to use
503     * @param orderBy The field_key(s) to order on and the corresponding direction(s)
504     * @param limit Max results to return for a single request (0-100 inclusive)
505     * @param marker The marker to use for requesting the next page
506     * @param fields The fields to retrieve.
507     * @return An iterable of BoxItem.Info search results
508     */
509    //CHECKSTYLE:OFF
510    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
511                                                            String from, String query, JsonObject queryParameters,
512                                                            String ancestorFolderId, String indexName,
513                                                            JsonArray orderBy, int limit, String marker,
514                                                            String ... fields) {
515    //CHECKSTYLE:ON
516        JsonObject jsonObject = createMetadataQueryBody(from, query, queryParameters, ancestorFolderId,
517                                                        indexName, orderBy, limit, marker, fields);
518
519        URL url = METADATA_QUERIES_URL_TEMPLATE.build(api.getBaseURL());
520        return new BoxResourceIterable<BoxItem.Info>(api, url, limit, jsonObject, marker) {
521
522            @Override
523            protected BoxItem.Info factory(JsonObject jsonObject) {
524                String type = jsonObject.get("type").asString();
525                String id = jsonObject.get("id").asString();
526
527                BoxItem.Info nextItemInfo;
528                if (type.equals("folder")) {
529                    BoxFolder folder = new BoxFolder(api, id);
530                    nextItemInfo = folder.new Info(jsonObject);
531                } else if (type.equals("file")) {
532                    BoxFile file = new BoxFile(api, id);
533                    nextItemInfo = file.new Info(jsonObject);
534                } else if (type.equals("web_link")) {
535                    BoxWebLink link = new BoxWebLink(api, id);
536                    nextItemInfo = link.new Info(jsonObject);
537                } else {
538                    assert false : "Unsupported item type: " + type;
539                    throw new BoxAPIException("Unsupported item type: " + type);
540                }
541
542                return nextItemInfo;
543            }
544        };
545    }
546
547    /**
548     * Create JSON body for metadata query.
549     *
550     * @param from The template used in the query. Must be in the form scope.templateKey
551     * @param query The logical expression of the query
552     * @param queryParameters Required if query present. The arguments for the query
553     * @param ancestorFolderId The folder_id to which to restrain the query
554     * @param indexName The name of the Index to use
555     * @param orderBy The field_key(s) to order on and the corresponding direction(s)
556     * @param limit Max results to return for a single request (0-100 inclusive)
557     * @param marker The marker to use for requesting the next page
558     * @param fields The fields to retrieve.
559     * @return A JSON object that is the body
560     */
561    private static JsonObject createMetadataQueryBody(String from, String query, JsonObject queryParameters,
562                                               String ancestorFolderId, String indexName,
563                                               JsonArray orderBy, int limit, String marker,
564                                               String ... fields) {
565        JsonObject jsonObject = new JsonObject().add("from", from);
566        if (query != null) {
567            jsonObject.add("query", query);
568        }
569        if (queryParameters != null) {
570            jsonObject.add("query_params", queryParameters);
571        }
572        if (ancestorFolderId != null) {
573            jsonObject.add("ancestor_folder_id", ancestorFolderId);
574        }
575        if (indexName != null) {
576            jsonObject.add("use_index", indexName);
577        }
578        if (orderBy != null) {
579            jsonObject.add("order_by", orderBy);
580        }
581        if (fields != null && fields.length > 0) {
582            JsonArray fieldsBody = new JsonArray();
583            for (String field : fields) {
584                fieldsBody.add(field);
585            }
586            jsonObject.add("fields", fieldsBody);
587        }
588        jsonObject.add("limit", limit);
589        if (marker != null) {
590            jsonObject.add("marker", marker);
591        }
592        return jsonObject;
593    }
594
595    /**
596     * Gets the JsonObject representation of the Field Operation.
597     * @param fieldOperation represents the template update operation
598     * @return the json object
599     */
600    private static JsonObject getFieldOperationJsonObject(FieldOperation fieldOperation) {
601        JsonObject jsonObject = new JsonObject();
602        jsonObject.add("op", fieldOperation.getOp().toString());
603
604        String fieldKey = fieldOperation.getFieldKey();
605        if (fieldKey != null) {
606            jsonObject.add("fieldKey", fieldKey);
607        }
608
609        Field field = fieldOperation.getData();
610        if (field != null) {
611            JsonObject fieldObj = new JsonObject();
612
613            String type = field.getType();
614            if (type != null) {
615                fieldObj.add("type", type);
616            }
617
618            String key = field.getKey();
619            if (key != null) {
620                fieldObj.add("key", key);
621            }
622
623            String displayName = field.getDisplayName();
624            if (displayName != null) {
625                fieldObj.add("displayName", displayName);
626            }
627
628            String description = field.getDescription();
629            if (description != null) {
630                fieldObj.add("description", description);
631            }
632
633            Boolean hidden = field.getIsHidden();
634            if (hidden != null) {
635                fieldObj.add("hidden", hidden);
636            }
637
638            List<String> options = field.getOptions();
639            if (options != null) {
640                JsonArray array = new JsonArray();
641                for (String option: options) {
642                    JsonObject optionObj = new JsonObject();
643                    optionObj.add("key", option);
644
645                    array.add(optionObj);
646                }
647
648                fieldObj.add("options", array);
649            }
650
651            Boolean copyInstanceOnItemCopy = field.getCopyInstanceOnItemCopy();
652            if (copyInstanceOnItemCopy != null) {
653                fieldObj.add("copyInstanceOnItemCopy", copyInstanceOnItemCopy);
654            }
655
656            jsonObject.add("data", fieldObj);
657        }
658
659        List<String> fieldKeys = fieldOperation.getFieldKeys();
660        if (fieldKeys != null) {
661            jsonObject.add("fieldKeys", getJsonArray(fieldKeys));
662        }
663
664        List<String> enumOptionKeys = fieldOperation.getEnumOptionKeys();
665        if (enumOptionKeys != null) {
666            jsonObject.add("enumOptionKeys", getJsonArray(enumOptionKeys));
667        }
668
669        String enumOptionKey = fieldOperation.getEnumOptionKey();
670        if (enumOptionKey != null) {
671            jsonObject.add("enumOptionKey", enumOptionKey);
672        }
673
674        String multiSelectOptionKey = fieldOperation.getMultiSelectOptionKey();
675        if (multiSelectOptionKey != null) {
676            jsonObject.add("multiSelectOptionKey", multiSelectOptionKey);
677        }
678
679        List<String> multiSelectOptionKeys = fieldOperation.getMultiSelectOptionKeys();
680        if (multiSelectOptionKeys != null) {
681            jsonObject.add("multiSelectOptionKeys", getJsonArray(multiSelectOptionKeys));
682        }
683
684        return jsonObject;
685    }
686
687    /**
688     * Gets the Json Array representation of the given list of strings.
689     * @param keys List of strings
690     * @return the JsonArray represents the list of keys
691     */
692    private static JsonArray getJsonArray(List<String> keys) {
693        JsonArray array = new JsonArray();
694        for (String key : keys) {
695            array.add(key);
696        }
697
698        return array;
699    }
700
701    /**
702     * Gets the metadata template of properties.
703     * @param api the API connection to be used.
704     * @return the metadata template returned from the server.
705     */
706    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api) {
707        return getMetadataTemplate(api, DEFAULT_METADATA_TYPE);
708    }
709
710    /**
711     * Gets the metadata template of specified template type.
712     * @param api the API connection to be used.
713     * @param templateName the metadata template type name.
714     * @return the metadata template returned from the server.
715     */
716    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api, String templateName) {
717        String scope = scopeBasedOnType(templateName);
718        return getMetadataTemplate(api, templateName, scope);
719    }
720
721    /**
722     * Gets the metadata template of specified template type.
723     * @param api the API connection to be used.
724     * @param templateName the metadata template type name.
725     * @param scope the metadata template scope (global or enterprise).
726     * @param fields the fields to retrieve.
727     * @return the metadata template returned from the server.
728     */
729    public static MetadataTemplate getMetadataTemplate(
730            BoxAPIConnection api, String templateName, String scope, String ... fields) {
731        QueryStringBuilder builder = new QueryStringBuilder();
732        if (fields.length > 0) {
733            builder.appendParam("fields", fields);
734        }
735        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlphaWithQuery(
736                api.getBaseURL(), builder.toString(), scope, templateName);
737        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
738        BoxJSONResponse response = (BoxJSONResponse) request.send();
739        return new MetadataTemplate(response.getJSON());
740    }
741
742    /**
743     * Geta the specified metadata template by its ID.
744     * @param api the API connection to be used.
745     * @param templateID the ID of the template to get.
746     * @return the metadata template object.
747     */
748    public static MetadataTemplate getMetadataTemplateByID(BoxAPIConnection api, String templateID) {
749
750        URL url = METADATA_TEMPLATE_BY_ID_URL_TEMPLATE.buildAlpha(api.getBaseURL(), templateID);
751        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
752        BoxJSONResponse response = (BoxJSONResponse) request.send();
753        return new MetadataTemplate(response.getJSON());
754    }
755
756    /**
757     * Returns all metadata templates within a user's enterprise.
758     * @param api the API connection to be used.
759     * @param fields the fields to retrieve.
760     * @return the metadata template returned from the server.
761     */
762    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(BoxAPIConnection api, String ... fields) {
763        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, api, fields);
764    }
765
766    /**
767     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
768     * @param scope the scope of the metadata templates.
769     * @param api the API connection to be used.
770     * @param fields the fields to retrieve.
771     * @return the metadata template returned from the server.
772     */
773    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
774            String scope, BoxAPIConnection api, String ... fields) {
775        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, DEFAULT_ENTRIES_LIMIT, api, fields);
776    }
777
778    /**
779     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
780     * @param scope the scope of the metadata templates.
781     * @param limit maximum number of entries per response.
782     * @param api the API connection to be used.
783     * @param fields the fields to retrieve.
784     * @return the metadata template returned from the server.
785     */
786    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
787            String scope, int limit, BoxAPIConnection api, String ... fields) {
788        QueryStringBuilder builder = new QueryStringBuilder();
789        if (fields.length > 0) {
790            builder.appendParam("fields", fields);
791        }
792        return new BoxResourceIterable<MetadataTemplate>(
793                api, ENTERPRISE_METADATA_URL_TEMPLATE.buildAlphaWithQuery(
794                        api.getBaseURL(), builder.toString(), scope), limit) {
795
796            @Override
797            protected MetadataTemplate factory(JsonObject jsonObject) {
798                return new MetadataTemplate(jsonObject);
799            }
800        };
801    }
802
803    /**
804     * Determines the metadata scope based on type.
805     * @param typeName type of the metadata.
806     * @return scope of the metadata.
807     */
808    private static String scopeBasedOnType(String typeName) {
809        return typeName.equals(DEFAULT_METADATA_TYPE) ? GLOBAL_METADATA_SCOPE : ENTERPRISE_METADATA_SCOPE;
810    }
811
812    /**
813     * Class contains information about the metadata template field.
814     */
815    public static class Field extends BoxJSONObject {
816
817        /**
818         * @see #getID()
819         */
820        private String id;
821
822        /**
823         * @see #getType()
824         */
825        private String type;
826
827        /**
828         * @see #getKey()
829         */
830        private String key;
831
832        /**
833         * @see #getDisplayName()
834         */
835        private String displayName;
836
837        /**
838         * @see #getIsHidden()
839         */
840        private Boolean isHidden;
841
842        /**
843         * @see #getDescription()
844         */
845        private String description;
846
847        /**
848         * @see #getOptionsObject()
849         */
850        private List<Option> options;
851
852        /**
853         * @see #getCopyInstanceOnItemCopy()
854         */
855        private Boolean copyInstanceOnItemCopy;
856
857        /**
858         * Constructs an empty metadata template.
859         */
860        public Field() {
861            super();
862        }
863
864        /**
865         * Constructs a metadate template field from a JSON string.
866         * @param json the json encoded metadate template field.
867         */
868        public Field(String json) {
869            super(json);
870        }
871
872        /**
873         * Constructs a metadate template field from a JSON object.
874         * @param jsonObject the json encoded metadate template field.
875         */
876        Field(JsonObject jsonObject) {
877            super(jsonObject);
878        }
879
880        /**
881         * Gets the ID of the template field.
882         * @return the template field ID.
883         */
884        public String getID() {
885            return this.id;
886        }
887
888        /**
889         * Gets the data type of the field's value.
890         * @return the data type of the field's value.
891         */
892        public String getType() {
893            return this.type;
894        }
895
896        /**
897         * Sets the data type of the field's value.
898         * @param type the data type of the field's value.
899         */
900        public void setType(String type) {
901            this.type = type;
902        }
903
904        /**
905         * Gets the key of the field.
906         * @return the key of the field.
907         */
908        public String getKey() {
909            return this.key;
910        }
911
912        /**
913         * Sets the key of the field.
914         * @param key the key of the field.
915         */
916        public void setKey(String key) {
917            this.key = key;
918        }
919
920        /**
921         * Gets the display name of the field.
922         * @return the display name of the field.
923         */
924        public String getDisplayName() {
925            return this.displayName;
926        }
927
928        /**
929         * Sets the display name of the field.
930         * @param displayName the display name of the field.
931         */
932        public void setDisplayName(String displayName) {
933            this.displayName = displayName;
934        }
935
936        /**
937         * Gets is metadata template field hidden.
938         * @return is metadata template field hidden.
939         */
940        public Boolean getIsHidden() {
941            return this.isHidden;
942        }
943
944        /**
945         * Sets is metadata template field hidden.
946         * @param isHidden is metadata template field hidden?
947         */
948        public void setIsHidden(boolean isHidden) {
949            this.isHidden = isHidden;
950        }
951
952        /**
953         * Gets the description of the field.
954         * @return the description of the field.
955         */
956        public String getDescription() {
957            return this.description;
958        }
959
960        /**
961         * Sets the description of the field.
962         * @param description the description of the field.
963         */
964        public void setDescription(String description) {
965            this.description = description;
966        }
967
968        /**
969         * Gets list of possible options for enum type of the field.
970         * @return list of possible options for enum type of the field.
971         */
972        public List<String> getOptions() {
973            if (this.options == null) {
974                return null;
975            }
976            List<String> optionsList = new ArrayList<String>();
977            for (Option option : this.options) {
978                optionsList.add(option.getKey());
979            }
980            return optionsList;
981        }
982
983        /**
984         * Gets list of possible options for options type of the field.
985         * @return list of possible options for option type of the field.
986         */
987        public List<Option> getOptionsObjects() {
988            return this.options;
989        }
990
991        /**
992         * Sets list of possible options for enum type of the field.
993         * @param options list of possible options for enum type of the field.
994         */
995        public void setOptions(List<String> options) {
996            if (options == null) {
997                this.options = null;
998            }
999            List<Option> optionList = new ArrayList<Option>();
1000            for (String key : options) {
1001                JsonObject optionObject = new JsonObject();
1002                optionObject.add("key", key);
1003                Option newOption = new Option(optionObject);
1004                optionList.add(newOption);
1005            }
1006            this.options = optionList;
1007        }
1008
1009        /**
1010         * Gets whether the copy operation should copy the metadata along with the item.
1011         * @return whether the copy operation should copy the metadata along with the item.
1012         */
1013        public Boolean getCopyInstanceOnItemCopy() {
1014            return this.copyInstanceOnItemCopy;
1015        }
1016
1017        /**
1018         * Sets whether the copy operation should copy the metadata along with the item.
1019         * @param copyInstanceOnItemCopy whether the copy operation should copy the metadata along with the item.
1020         */
1021        public void setCopyInstanceOnItemCopy(Boolean copyInstanceOnItemCopy) {
1022            this.copyInstanceOnItemCopy = copyInstanceOnItemCopy;
1023        }
1024
1025        /**
1026         * {@inheritDoc}
1027         */
1028        @Override
1029        void parseJSONMember(JsonObject.Member member) {
1030            JsonValue value = member.getValue();
1031            String memberName = member.getName();
1032            if (memberName.equals("type")) {
1033                this.type = value.asString();
1034            } else if (memberName.equals("key")) {
1035                this.key = value.asString();
1036            } else if (memberName.equals("displayName")) {
1037                this.displayName = value.asString();
1038            } else if (memberName.equals("hidden")) {
1039                this.isHidden = value.asBoolean();
1040            } else if (memberName.equals("description")) {
1041                this.description = value.asString();
1042            } else if (memberName.equals("options")) {
1043                this.options = new ArrayList<Option>();
1044                for (JsonValue option : value.asArray()) {
1045                    this.options.add(new Option(option.asObject()));
1046                }
1047            } else if (memberName.equals("id")) {
1048                this.id = value.asString();
1049            } else if (memberName.equals("copyInstanceOnItemCopy")) {
1050                this.copyInstanceOnItemCopy = value.asBoolean();
1051            }
1052        }
1053    }
1054
1055    /**
1056     * Class contains information about the metadata template option.
1057     */
1058    public static class Option extends BoxJSONObject {
1059        /**
1060         * @see #getID()
1061         */
1062        private String id;
1063         /**
1064         * @see #getKey()
1065         */
1066        private String key;
1067         /**
1068         * Constructs an empty metadata template.
1069         */
1070        public Option() {
1071            super();
1072        }
1073         /**
1074         * Constructs a metadate template option from a JSON string.
1075         * @param json the json encoded metadata template option.
1076         */
1077        public Option(String json) {
1078            super(json);
1079        }
1080
1081         /**
1082         * Constructs a metadate template option from a JSON object.
1083         * @param jsonObject the json encoded metadate template option.
1084         */
1085        Option(JsonObject jsonObject) {
1086            super(jsonObject);
1087        }
1088         /**
1089         * Gets the ID of the template field.
1090         * @return the template field ID.
1091         */
1092        public String getID() {
1093            return this.id;
1094        }
1095         /**
1096         * Gets the key of the field.
1097         * @return the key of the field.
1098         */
1099        public String getKey() {
1100            return this.key;
1101        }
1102
1103        /**
1104         * {@inheritDoc}
1105         */
1106        @Override
1107        void parseJSONMember(JsonObject.Member member) {
1108            JsonValue value = member.getValue();
1109            String memberName = member.getName();
1110            if (memberName.equals("id")) {
1111                this.id = value.asString();
1112            } else if (memberName.equals("key")) {
1113                this.key = value.asString();
1114            }
1115        }
1116    }
1117
1118    /**
1119     * Posssible operations that can be performed in a Metadata template.
1120     *  <ul>
1121     *      <li>Add an enum option</li>
1122     *      <li>Edit an enum option</li>
1123     *      <li>Remove an enum option</li>
1124     *      <li>Add a field</li>
1125     *      <li>Edit a field</li>
1126     *      <li>Remove a field</li>
1127     *      <li>Edit template</li>
1128     *      <li>Reorder the enum option</li>
1129     *      <li>Reorder the field list</li>
1130     *  </ul>
1131     */
1132    public static class FieldOperation extends BoxJSONObject {
1133
1134        private Operation op;
1135        private Field data;
1136        private String fieldKey;
1137        private List<String> fieldKeys;
1138        private List<String> enumOptionKeys;
1139        private String enumOptionKey;
1140        private String multiSelectOptionKey;
1141        private List<String> multiSelectOptionKeys;
1142
1143        /**
1144         * Constructs an empty FieldOperation.
1145         */
1146        public FieldOperation() {
1147            super();
1148        }
1149
1150        /**
1151         * Constructs a Field operation from a JSON string.
1152         * @param json the json encoded metadate template field.
1153         */
1154        public FieldOperation(String json) {
1155            super(json);
1156        }
1157
1158        /**
1159         * Constructs a Field operation from a JSON object.
1160         * @param jsonObject the json encoded metadate template field.
1161         */
1162        FieldOperation(JsonObject jsonObject) {
1163            super(jsonObject);
1164        }
1165
1166        /**
1167         * Gets the operation.
1168         * @return the operation
1169         */
1170        public Operation getOp() {
1171            return this.op;
1172        }
1173
1174        /**
1175         * Gets the data associated with the operation.
1176         * @return the field object representing the data
1177         */
1178        public Field getData() {
1179            return this.data;
1180        }
1181
1182        /**
1183         * Gets the field key.
1184         * @return the field key
1185         */
1186        public String getFieldKey() {
1187            return this.fieldKey;
1188        }
1189
1190        /**
1191         * Gets the list of field keys.
1192         * @return the list of Strings
1193         */
1194        public List<String> getFieldKeys() {
1195            return this.fieldKeys;
1196        }
1197
1198        /**
1199         * Gets the list of keys of the Enum options.
1200         * @return the list of Strings
1201         */
1202        public List<String> getEnumOptionKeys() {
1203            return this.enumOptionKeys;
1204        }
1205
1206        /**
1207         * Sets the operation.
1208         * @param op the operation
1209         */
1210        public void setOp(Operation op) {
1211            this.op = op;
1212        }
1213
1214        /**
1215         * Sets the data.
1216         * @param data the Field object representing the data
1217         */
1218        public void setData(Field data) {
1219            this.data = data;
1220        }
1221
1222        /**
1223         * Sets the field key.
1224         * @param fieldKey the key of the field
1225         */
1226        public void setFieldKey(String fieldKey) {
1227            this.fieldKey = fieldKey;
1228        }
1229
1230        /**
1231         * Sets the list of the field keys.
1232         * @param fieldKeys the list of strings
1233         */
1234        public void setFieldKeys(List<String> fieldKeys) {
1235            this.fieldKeys = fieldKeys;
1236        }
1237
1238        /**
1239         * Sets the list of the enum option keys.
1240         * @param enumOptionKeys the list of Strings
1241         */
1242        public void setEnumOptionKeys(List<String> enumOptionKeys) {
1243            this.enumOptionKeys = enumOptionKeys;
1244        }
1245
1246        /**
1247         * Gets the enum option key.
1248         * @return the enum option key
1249         */
1250        public String getEnumOptionKey() {
1251            return this.enumOptionKey;
1252        }
1253
1254        /**
1255         * Sets the enum option key.
1256         * @param enumOptionKey the enum option key
1257         */
1258        public void setEnumOptionKey(String enumOptionKey) {
1259            this.enumOptionKey = enumOptionKey;
1260        }
1261
1262        /**
1263         * Gets the multi-select option key.
1264         * @return the key.
1265         */
1266        public String getMultiSelectOptionKey() {
1267            return this.multiSelectOptionKey;
1268        }
1269
1270        /**
1271         * Sets the multi-select option key.
1272         * @param key the key.
1273         */
1274        public void setMultiSelectOptionKey(String key) {
1275            this.multiSelectOptionKey = key;
1276        }
1277
1278        /**
1279         * Gets the list of multiselect option keys.
1280         * @return the list of keys.
1281         */
1282        public List<String> getMultiSelectOptionKeys() {
1283            return this.multiSelectOptionKeys;
1284        }
1285
1286        /**
1287         * Sets the multi-select option keys.
1288         * @param keys the list of keys.
1289         */
1290        public void setMultiSelectOptionKeys(List<String> keys) {
1291            this.multiSelectOptionKeys = keys;
1292        }
1293
1294        /**
1295         * {@inheritDoc}
1296         */
1297        @Override
1298        void parseJSONMember(JsonObject.Member member) {
1299            JsonValue value = member.getValue();
1300            String memberName = member.getName();
1301            if (memberName.equals("op")) {
1302                this.op = Operation.valueOf(value.asString());
1303            } else if (memberName.equals("data")) {
1304                this.data = new Field(value.asObject());
1305            } else if (memberName.equals("fieldKey")) {
1306                this.fieldKey = value.asString();
1307            } else if (memberName.equals("fieldKeys")) {
1308                if (this.fieldKeys == null) {
1309                    this.fieldKeys = new ArrayList<String>();
1310                } else {
1311                    this.fieldKeys.clear();
1312                }
1313
1314                JsonArray array = value.asArray();
1315                for (JsonValue jsonValue: array) {
1316                    this.fieldKeys.add(jsonValue.asString());
1317                }
1318            } else if (memberName.equals("enumOptionKeys")) {
1319                if (this.enumOptionKeys == null) {
1320                    this.enumOptionKeys = new ArrayList<String>();
1321                } else {
1322                    this.enumOptionKeys.clear();
1323                }
1324
1325                JsonArray array = value.asArray();
1326                for (JsonValue jsonValue: array) {
1327                    this.enumOptionKeys.add(jsonValue.asString());
1328                }
1329            } else if (memberName.equals("enumOptionKey")) {
1330                this.enumOptionKey = value.asString();
1331            } else if (memberName.equals("multiSelectOptionKey")) {
1332                this.multiSelectOptionKey = value.asString();
1333            } else if (memberName.equals("multiSelectOptionKeys")) {
1334                this.multiSelectOptionKeys = new ArrayList<String>();
1335                for (JsonValue key : value.asArray()) {
1336                    this.multiSelectOptionKeys.add(key.asString());
1337                }
1338            }
1339        }
1340    }
1341
1342    /**
1343     * Possible template operations.
1344     */
1345    public enum Operation {
1346
1347        /**
1348         * Adds an enum option at the end of the enum option list for the specified field.
1349         */
1350        addEnumOption,
1351
1352        /**
1353         * Edits the enum option.
1354         */
1355        editEnumOption,
1356
1357        /**
1358         * Removes the specified enum option from the specified enum field.
1359         */
1360        removeEnumOption,
1361
1362        /**
1363         * Adds a field at the end of the field list for the template.
1364         */
1365        addField,
1366
1367        /**
1368         * Edits any number of the base properties of a field: displayName, hidden, description.
1369         */
1370        editField,
1371
1372        /**
1373         * Removes the specified field from the template.
1374         */
1375        removeField,
1376
1377        /**
1378         * Edits any number of the base properties of a template: displayName, hidden.
1379         */
1380        editTemplate,
1381
1382        /**
1383         * Reorders the enum option list to match the requested enum option list.
1384         */
1385        reorderEnumOptions,
1386
1387        /**
1388         * Reorders the field list to match the requested field list.
1389         */
1390        reorderFields,
1391
1392        /**
1393         * Adds a new option to a multiselect field.
1394         */
1395        addMultiSelectOption,
1396
1397        /**
1398         * Edits an existing option in a multiselect field.
1399         */
1400        editMultiSelectOption,
1401
1402        /**
1403         * Removes an option from a multiselect field.
1404         */
1405        removeMultiSelectOption,
1406
1407        /**
1408         * Changes the display order of options in a multiselect field.
1409         */
1410        reorderMultiSelectOptions
1411    }
1412}