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://docs.box.com/reference#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     */
363    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
364                                                            String from, String ancestorFolderId) {
365        return executeMetadataQuery(api, from, null, null, ancestorFolderId, null, null, 100, null);
366    }
367
368    /**
369     * Executes a metadata query.
370     *
371     * @param api The API connection to be used
372     * @param from The template used in the query. Must be in the form scope.templateKey
373     * @param ancestorFolderId The folder_id to which to restrain the query
374     * @param fields The fields to retrieve.
375     * @return An iterable of BoxItem.Info search results
376     */
377    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
378                                                            String from, String ancestorFolderId, String ... fields) {
379        return executeMetadataQuery(api, from, null, null, ancestorFolderId, null, null, 100, null, fields);
380    }
381
382    /**
383      * Executes a metadata query.
384      *
385      * @param api The API connection to be used
386      * @param from The template used in the query. Must be in the form scope.templateKey
387      * @param query The logical expression of the query
388      * @param queryParameters Required if query present. The arguments for the query
389      * @param ancestorFolderId The folder_id to which to restrain the query
390      * @return An iterable of BoxMetadataQueryItem search results
391      */
392    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
393                                                            String from, String query, JsonObject queryParameters,
394                                                            String ancestorFolderId) {
395        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, null, null, 100, null);
396    }
397
398    /**
399      * Executes a metadata query.
400      *
401      * @param api The API connection to be used
402      * @param from The template used in the query. Must be in the form scope.templateKey
403      * @param query The logical expression of the query
404      * @param queryParameters Required if query present. The arguments for the query
405      * @param ancestorFolderId The folder_id to which to restrain the query
406      * @param fields The fields to retrieve.
407      * @return An iterable of BoxItem.Info search results
408      */
409    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
410                                                            String from, String query, JsonObject queryParameters,
411                                                            String ancestorFolderId, String ... fields) {
412        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, null, null, 100, null, fields);
413    }
414
415    /**
416      * Executes a metadata query.
417      *
418      * @param api The API connection to be used
419      * @param from The template used in the query. Must be in the form scope.templateKey
420      * @param query The logical expression of the query
421      * @param queryParameters Required if query present. The arguments for the query
422      * @param ancestorFolderId The folder_id to which to restrain the query
423      * @param indexName The name of the Index to use
424      * @param orderBy The field_key(s) to order on and the corresponding direction(s)
425      * @return An iterable of BoxMetadataQueryItem search results
426      */
427    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
428                                                            String from, String query, JsonObject queryParameters,
429                                                            String ancestorFolderId, String indexName,
430                                                            JsonArray orderBy) {
431        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, indexName, orderBy, 100, null);
432    }
433
434    /**
435      * Executes a metadata query.
436      *
437      * @param api The API connection to be used
438      * @param from The template used in the query. Must be in the form scope.templateKey
439      * @param query The logical expression of the query
440      * @param queryParameters Required if query present. The arguments for the query
441      * @param ancestorFolderId The folder_id to which to restrain the query
442      * @param indexName The name of the Index to use
443      * @param orderBy The field_key(s) to order on and the corresponding direction(s)
444      * @param fields The fields to retrieve.
445      * @return An iterable of BoxItem.Info search results
446      */
447    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
448                                                            String from, String query, JsonObject queryParameters,
449                                                            String ancestorFolderId, String indexName,
450                                                            JsonArray orderBy, String ... fields) {
451        return executeMetadataQuery(api, from, query, queryParameters, ancestorFolderId, indexName, orderBy, 100,
452                            null, fields);
453    }
454
455    /**
456     * Executes a metadata query.
457     *
458     * @param api The API connection to be used
459     * @param from The template used in the query. Must be in the form scope.templateKey
460     * @param query The logical expression of the query
461     * @param queryParameters Required if query present. The arguments for the query
462     * @param ancestorFolderId The folder_id to which to restrain the query
463     * @param indexName The name of the Index to use
464     * @param orderBy The field_key(s) to order on and the corresponding direction(s)
465     * @param limit Max results to return for a single request (0-100 inclusive)
466     * @param marker The marker to use for requesting the next page
467     * @return An iterable of BoxMetadataQueryItem search results
468     */
469    public static BoxResourceIterable<BoxMetadataQueryItem> executeMetadataQuery(final BoxAPIConnection api,
470                                                            String from, String query, JsonObject queryParameters,
471                                                            String ancestorFolderId, String indexName,
472                                                            JsonArray orderBy, int limit, String marker) {
473        JsonObject jsonObject = createMetadataQueryBody(from, query, queryParameters, ancestorFolderId,
474                                                        indexName, orderBy, limit, marker);
475
476        URL url = METADATA_QUERIES_URL_TEMPLATE.build(api.getBaseURL());
477        return new BoxResourceIterable<BoxMetadataQueryItem>(api, url, limit, jsonObject, marker) {
478
479            @Override
480            protected BoxMetadataQueryItem factory(JsonObject jsonObject) {
481                return new BoxMetadataQueryItem(jsonObject, api);
482            }
483        };
484    }
485
486    /**
487     * Executes a metadata query.
488     *
489     * @param api The API connection to be used
490     * @param from The template used in the query. Must be in the form scope.templateKey
491     * @param query The logical expression of the query
492     * @param queryParameters Required if query present. The arguments for the query
493     * @param ancestorFolderId The folder_id to which to restrain the query
494     * @param indexName The name of the Index to use
495     * @param orderBy The field_key(s) to order on and the corresponding direction(s)
496     * @param limit Max results to return for a single request (0-100 inclusive)
497     * @param marker The marker to use for requesting the next page
498     * @param fields The fields to retrieve.
499     * @return An iterable of BoxItem.Info search results
500     */
501    //CHECKSTYLE:OFF
502    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
503                                                            String from, String query, JsonObject queryParameters,
504                                                            String ancestorFolderId, String indexName,
505                                                            JsonArray orderBy, int limit, String marker,
506                                                            String ... fields) {
507    //CHECKSTYLE:ON
508        JsonObject jsonObject = createMetadataQueryBody(from, query, queryParameters, ancestorFolderId,
509                                                        indexName, orderBy, limit, marker, fields);
510
511        URL url = METADATA_QUERIES_URL_TEMPLATE.build(api.getBaseURL());
512        return new BoxResourceIterable<BoxItem.Info>(api, url, limit, jsonObject, marker) {
513
514            @Override
515            protected BoxItem.Info factory(JsonObject jsonObject) {
516                String type = jsonObject.get("type").asString();
517                String id = jsonObject.get("id").asString();
518
519                BoxItem.Info nextItemInfo;
520                if (type.equals("folder")) {
521                    BoxFolder folder = new BoxFolder(api, id);
522                    nextItemInfo = folder.new Info(jsonObject);
523                } else if (type.equals("file")) {
524                    BoxFile file = new BoxFile(api, id);
525                    nextItemInfo = file.new Info(jsonObject);
526                } else if (type.equals("web_link")) {
527                    BoxWebLink link = new BoxWebLink(api, id);
528                    nextItemInfo = link.new Info(jsonObject);
529                } else {
530                    assert false : "Unsupported item type: " + type;
531                    throw new BoxAPIException("Unsupported item type: " + type);
532                }
533
534                return nextItemInfo;
535            }
536        };
537    }
538
539    /**
540     * Create JSON body for metadata query.
541     *
542     * @param from The template used in the query. Must be in the form scope.templateKey
543     * @param query The logical expression of the query
544     * @param queryParameters Required if query present. The arguments for the query
545     * @param ancestorFolderId The folder_id to which to restrain the query
546     * @param indexName The name of the Index to use
547     * @param orderBy The field_key(s) to order on and the corresponding direction(s)
548     * @param limit Max results to return for a single request (0-100 inclusive)
549     * @param marker The marker to use for requesting the next page
550     * @param fields The fields to retrieve.
551     * @return A JSON object that is the body
552     */
553    private static JsonObject createMetadataQueryBody(String from, String query, JsonObject queryParameters,
554                                               String ancestorFolderId, String indexName,
555                                               JsonArray orderBy, int limit, String marker,
556                                               String ... fields) {
557        JsonObject jsonObject = new JsonObject().add("from", from);
558        if (query != null) {
559            jsonObject.add("query", query);
560        }
561        if (queryParameters != null) {
562            jsonObject.add("query_params", queryParameters);
563        }
564        if (ancestorFolderId != null) {
565            jsonObject.add("ancestor_folder_id", ancestorFolderId);
566        }
567        if (indexName != null) {
568            jsonObject.add("use_index", indexName);
569        }
570        if (orderBy != null) {
571            jsonObject.add("order_by", orderBy);
572        }
573        if (fields.length > 0) {
574            JsonArray fieldsBody = new JsonArray();
575            for (String field : fields) {
576                fieldsBody.add(field);
577            }
578            jsonObject.add("fields", fieldsBody);
579        }
580        jsonObject.add("limit", limit);
581        if (marker != null) {
582            jsonObject.add("marker", marker);
583        }
584        return jsonObject;
585    }
586
587    /**
588     * Gets the JsonObject representation of the Field Operation.
589     * @param fieldOperation represents the template update operation
590     * @return the json object
591     */
592    private static JsonObject getFieldOperationJsonObject(FieldOperation fieldOperation) {
593        JsonObject jsonObject = new JsonObject();
594        jsonObject.add("op", fieldOperation.getOp().toString());
595
596        String fieldKey = fieldOperation.getFieldKey();
597        if (fieldKey != null) {
598            jsonObject.add("fieldKey", fieldKey);
599        }
600
601        Field field = fieldOperation.getData();
602        if (field != null) {
603            JsonObject fieldObj = new JsonObject();
604
605            String type = field.getType();
606            if (type != null) {
607                fieldObj.add("type", type);
608            }
609
610            String key = field.getKey();
611            if (key != null) {
612                fieldObj.add("key", key);
613            }
614
615            String displayName = field.getDisplayName();
616            if (displayName != null) {
617                fieldObj.add("displayName", displayName);
618            }
619
620            String description = field.getDescription();
621            if (description != null) {
622                fieldObj.add("description", description);
623            }
624
625            Boolean hidden = field.getIsHidden();
626            if (hidden != null) {
627                fieldObj.add("hidden", hidden);
628            }
629
630            List<String> options = field.getOptions();
631            if (options != null) {
632                JsonArray array = new JsonArray();
633                for (String option: options) {
634                    JsonObject optionObj = new JsonObject();
635                    optionObj.add("key", option);
636
637                    array.add(optionObj);
638                }
639
640                fieldObj.add("options", array);
641            }
642
643            Boolean copyInstanceOnItemCopy = field.getCopyInstanceOnItemCopy();
644            if (copyInstanceOnItemCopy != null) {
645                fieldObj.add("copyInstanceOnItemCopy", copyInstanceOnItemCopy);
646            }
647
648            jsonObject.add("data", fieldObj);
649        }
650
651        List<String> fieldKeys = fieldOperation.getFieldKeys();
652        if (fieldKeys != null) {
653            jsonObject.add("fieldKeys", getJsonArray(fieldKeys));
654        }
655
656        List<String> enumOptionKeys = fieldOperation.getEnumOptionKeys();
657        if (enumOptionKeys != null) {
658            jsonObject.add("enumOptionKeys", getJsonArray(enumOptionKeys));
659        }
660
661        String enumOptionKey = fieldOperation.getEnumOptionKey();
662        if (enumOptionKey != null) {
663            jsonObject.add("enumOptionKey", enumOptionKey);
664        }
665
666        String multiSelectOptionKey = fieldOperation.getMultiSelectOptionKey();
667        if (multiSelectOptionKey != null) {
668            jsonObject.add("multiSelectOptionKey", multiSelectOptionKey);
669        }
670
671        List<String> multiSelectOptionKeys = fieldOperation.getMultiSelectOptionKeys();
672        if (multiSelectOptionKeys != null) {
673            jsonObject.add("multiSelectOptionKeys", getJsonArray(multiSelectOptionKeys));
674        }
675
676        return jsonObject;
677    }
678
679    /**
680     * Gets the Json Array representation of the given list of strings.
681     * @param keys List of strings
682     * @return the JsonArray represents the list of keys
683     */
684    private static JsonArray getJsonArray(List<String> keys) {
685        JsonArray array = new JsonArray();
686        for (String key : keys) {
687            array.add(key);
688        }
689
690        return array;
691    }
692
693    /**
694     * Gets the metadata template of properties.
695     * @param api the API connection to be used.
696     * @return the metadata template returned from the server.
697     */
698    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api) {
699        return getMetadataTemplate(api, DEFAULT_METADATA_TYPE);
700    }
701
702    /**
703     * Gets the metadata template of specified template type.
704     * @param api the API connection to be used.
705     * @param templateName the metadata template type name.
706     * @return the metadata template returned from the server.
707     */
708    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api, String templateName) {
709        String scope = scopeBasedOnType(templateName);
710        return getMetadataTemplate(api, templateName, scope);
711    }
712
713    /**
714     * Gets the metadata template of specified template type.
715     * @param api the API connection to be used.
716     * @param templateName the metadata template type name.
717     * @param scope the metadata template scope (global or enterprise).
718     * @param fields the fields to retrieve.
719     * @return the metadata template returned from the server.
720     */
721    public static MetadataTemplate getMetadataTemplate(
722            BoxAPIConnection api, String templateName, String scope, String ... fields) {
723        QueryStringBuilder builder = new QueryStringBuilder();
724        if (fields.length > 0) {
725            builder.appendParam("fields", fields);
726        }
727        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlphaWithQuery(
728                api.getBaseURL(), builder.toString(), scope, templateName);
729        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
730        BoxJSONResponse response = (BoxJSONResponse) request.send();
731        return new MetadataTemplate(response.getJSON());
732    }
733
734    /**
735     * Geta the specified metadata template by its ID.
736     * @param api the API connection to be used.
737     * @param templateID the ID of the template to get.
738     * @return the metadata template object.
739     */
740    public static MetadataTemplate getMetadataTemplateByID(BoxAPIConnection api, String templateID) {
741
742        URL url = METADATA_TEMPLATE_BY_ID_URL_TEMPLATE.buildAlpha(api.getBaseURL(), templateID);
743        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
744        BoxJSONResponse response = (BoxJSONResponse) request.send();
745        return new MetadataTemplate(response.getJSON());
746    }
747
748    /**
749     * Returns all metadata templates within a user's enterprise.
750     * @param api the API connection to be used.
751     * @param fields the fields to retrieve.
752     * @return the metadata template returned from the server.
753     */
754    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(BoxAPIConnection api, String ... fields) {
755        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, api, fields);
756    }
757
758    /**
759     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
760     * @param scope the scope of the metadata templates.
761     * @param api the API connection to be used.
762     * @param fields the fields to retrieve.
763     * @return the metadata template returned from the server.
764     */
765    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
766            String scope, BoxAPIConnection api, String ... fields) {
767        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, DEFAULT_ENTRIES_LIMIT, api, fields);
768    }
769
770    /**
771     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
772     * @param scope the scope of the metadata templates.
773     * @param limit maximum number of entries per response.
774     * @param api the API connection to be used.
775     * @param fields the fields to retrieve.
776     * @return the metadata template returned from the server.
777     */
778    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
779            String scope, int limit, BoxAPIConnection api, String ... fields) {
780        QueryStringBuilder builder = new QueryStringBuilder();
781        if (fields.length > 0) {
782            builder.appendParam("fields", fields);
783        }
784        return new BoxResourceIterable<MetadataTemplate>(
785                api, ENTERPRISE_METADATA_URL_TEMPLATE.buildAlphaWithQuery(
786                        api.getBaseURL(), builder.toString(), scope), limit) {
787
788            @Override
789            protected MetadataTemplate factory(JsonObject jsonObject) {
790                return new MetadataTemplate(jsonObject);
791            }
792        };
793    }
794
795    /**
796     * Determines the metadata scope based on type.
797     * @param typeName type of the metadata.
798     * @return scope of the metadata.
799     */
800    private static String scopeBasedOnType(String typeName) {
801        return typeName.equals(DEFAULT_METADATA_TYPE) ? GLOBAL_METADATA_SCOPE : ENTERPRISE_METADATA_SCOPE;
802    }
803
804    /**
805     * Class contains information about the metadata template field.
806     */
807    public static class Field extends BoxJSONObject {
808
809        /**
810         * @see #getID()
811         */
812        private String id;
813
814        /**
815         * @see #getType()
816         */
817        private String type;
818
819        /**
820         * @see #getKey()
821         */
822        private String key;
823
824        /**
825         * @see #getDisplayName()
826         */
827        private String displayName;
828
829        /**
830         * @see #getIsHidden()
831         */
832        private Boolean isHidden;
833
834        /**
835         * @see #getDescription()
836         */
837        private String description;
838
839        /**
840         * @see #getOptionsObject()
841         */
842        private List<Option> options;
843
844        /**
845         * @see #getCopyInstanceOnItemCopy()
846         */
847        private Boolean copyInstanceOnItemCopy;
848
849        /**
850         * Constructs an empty metadata template.
851         */
852        public Field() {
853            super();
854        }
855
856        /**
857         * Constructs a metadate template field from a JSON string.
858         * @param json the json encoded metadate template field.
859         */
860        public Field(String json) {
861            super(json);
862        }
863
864        /**
865         * Constructs a metadate template field from a JSON object.
866         * @param jsonObject the json encoded metadate template field.
867         */
868        Field(JsonObject jsonObject) {
869            super(jsonObject);
870        }
871
872        /**
873         * Gets the ID of the template field.
874         * @return the template field ID.
875         */
876        public String getID() {
877            return this.id;
878        }
879
880        /**
881         * Gets the data type of the field's value.
882         * @return the data type of the field's value.
883         */
884        public String getType() {
885            return this.type;
886        }
887
888        /**
889         * Sets the data type of the field's value.
890         * @param type the data type of the field's value.
891         */
892        public void setType(String type) {
893            this.type = type;
894        }
895
896        /**
897         * Gets the key of the field.
898         * @return the key of the field.
899         */
900        public String getKey() {
901            return this.key;
902        }
903
904        /**
905         * Sets the key of the field.
906         * @param key the key of the field.
907         */
908        public void setKey(String key) {
909            this.key = key;
910        }
911
912        /**
913         * Gets the display name of the field.
914         * @return the display name of the field.
915         */
916        public String getDisplayName() {
917            return this.displayName;
918        }
919
920        /**
921         * Sets the display name of the field.
922         * @param displayName the display name of the field.
923         */
924        public void setDisplayName(String displayName) {
925            this.displayName = displayName;
926        }
927
928        /**
929         * Gets is metadata template field hidden.
930         * @return is metadata template field hidden.
931         */
932        public Boolean getIsHidden() {
933            return this.isHidden;
934        }
935
936        /**
937         * Sets is metadata template field hidden.
938         * @param isHidden is metadata template field hidden?
939         */
940        public void setIsHidden(boolean isHidden) {
941            this.isHidden = isHidden;
942        }
943
944        /**
945         * Gets the description of the field.
946         * @return the description of the field.
947         */
948        public String getDescription() {
949            return this.description;
950        }
951
952        /**
953         * Sets the description of the field.
954         * @param description the description of the field.
955         */
956        public void setDescription(String description) {
957            this.description = description;
958        }
959
960        /**
961         * Gets list of possible options for enum type of the field.
962         * @return list of possible options for enum type of the field.
963         */
964        public List<String> getOptions() {
965            if (this.options == null) {
966                return null;
967            }
968            List<String> optionsList = new ArrayList<String>();
969            for (Option option : this.options) {
970                optionsList.add(option.getKey());
971            }
972            return optionsList;
973        }
974
975        /**
976         * Gets list of possible options for options type of the field.
977         * @return list of possible options for option type of the field.
978         */
979        public List<Option> getOptionsObjects() {
980            return this.options;
981        }
982
983        /**
984         * Sets list of possible options for enum type of the field.
985         * @param options list of possible options for enum type of the field.
986         */
987        public void setOptions(List<String> options) {
988            if (options == null) {
989                this.options = null;
990            }
991            List<Option> optionList = new ArrayList<Option>();
992            for (String key : options) {
993                JsonObject optionObject = new JsonObject();
994                optionObject.add("key", key);
995                Option newOption = new Option(optionObject);
996                optionList.add(newOption);
997            }
998            this.options = optionList;
999        }
1000
1001        /**
1002         * Gets whether the copy operation should copy the metadata along with the item.
1003         * @return whether the copy operation should copy the metadata along with the item.
1004         */
1005        public Boolean getCopyInstanceOnItemCopy() {
1006            return this.copyInstanceOnItemCopy;
1007        }
1008
1009        /**
1010         * Sets whether the copy operation should copy the metadata along with the item.
1011         * @param copyInstanceOnItemCopy whether the copy operation should copy the metadata along with the item.
1012         */
1013        public void setCopyInstanceOnItemCopy(Boolean copyInstanceOnItemCopy) {
1014            this.copyInstanceOnItemCopy = copyInstanceOnItemCopy;
1015        }
1016
1017        /**
1018         * {@inheritDoc}
1019         */
1020        @Override
1021        void parseJSONMember(JsonObject.Member member) {
1022            JsonValue value = member.getValue();
1023            String memberName = member.getName();
1024            if (memberName.equals("type")) {
1025                this.type = value.asString();
1026            } else if (memberName.equals("key")) {
1027                this.key = value.asString();
1028            } else if (memberName.equals("displayName")) {
1029                this.displayName = value.asString();
1030            } else if (memberName.equals("hidden")) {
1031                this.isHidden = value.asBoolean();
1032            } else if (memberName.equals("description")) {
1033                this.description = value.asString();
1034            } else if (memberName.equals("options")) {
1035                this.options = new ArrayList<Option>();
1036                for (JsonValue option : value.asArray()) {
1037                    this.options.add(new Option(option.asObject()));
1038                }
1039            } else if (memberName.equals("id")) {
1040                this.id = value.asString();
1041            } else if (memberName.equals("copyInstanceOnItemCopy")) {
1042                this.copyInstanceOnItemCopy = value.asBoolean();
1043            }
1044        }
1045    }
1046
1047    /**
1048     * Class contains information about the metadata template option.
1049     */
1050    public static class Option extends BoxJSONObject {
1051        /**
1052         * @see #getID()
1053         */
1054        private String id;
1055         /**
1056         * @see #getKey()
1057         */
1058        private String key;
1059         /**
1060         * Constructs an empty metadata template.
1061         */
1062        public Option() {
1063            super();
1064        }
1065         /**
1066         * Constructs a metadate template option from a JSON string.
1067         * @param json the json encoded metadata template option.
1068         */
1069        public Option(String json) {
1070            super(json);
1071        }
1072
1073         /**
1074         * Constructs a metadate template option from a JSON object.
1075         * @param jsonObject the json encoded metadate template option.
1076         */
1077        Option(JsonObject jsonObject) {
1078            super(jsonObject);
1079        }
1080         /**
1081         * Gets the ID of the template field.
1082         * @return the template field ID.
1083         */
1084        public String getID() {
1085            return this.id;
1086        }
1087         /**
1088         * Gets the key of the field.
1089         * @return the key of the field.
1090         */
1091        public String getKey() {
1092            return this.key;
1093        }
1094
1095        /**
1096         * {@inheritDoc}
1097         */
1098        @Override
1099        void parseJSONMember(JsonObject.Member member) {
1100            JsonValue value = member.getValue();
1101            String memberName = member.getName();
1102            if (memberName.equals("id")) {
1103                this.id = value.asString();
1104            } else if (memberName.equals("key")) {
1105                this.key = value.asString();
1106            }
1107        }
1108    }
1109
1110    /**
1111     * Posssible operations that can be performed in a Metadata template.
1112     *  <ul>
1113     *      <li>Add an enum option</li>
1114     *      <li>Edit an enum option</li>
1115     *      <li>Remove an enum option</li>
1116     *      <li>Add a field</li>
1117     *      <li>Edit a field</li>
1118     *      <li>Remove a field</li>
1119     *      <li>Edit template</li>
1120     *      <li>Reorder the enum option</li>
1121     *      <li>Reorder the field list</li>
1122     *  </ul>
1123     */
1124    public static class FieldOperation extends BoxJSONObject {
1125
1126        private Operation op;
1127        private Field data;
1128        private String fieldKey;
1129        private List<String> fieldKeys;
1130        private List<String> enumOptionKeys;
1131        private String enumOptionKey;
1132        private String multiSelectOptionKey;
1133        private List<String> multiSelectOptionKeys;
1134
1135        /**
1136         * Constructs an empty FieldOperation.
1137         */
1138        public FieldOperation() {
1139            super();
1140        }
1141
1142        /**
1143         * Constructs a Field operation from a JSON string.
1144         * @param json the json encoded metadate template field.
1145         */
1146        public FieldOperation(String json) {
1147            super(json);
1148        }
1149
1150        /**
1151         * Constructs a Field operation from a JSON object.
1152         * @param jsonObject the json encoded metadate template field.
1153         */
1154        FieldOperation(JsonObject jsonObject) {
1155            super(jsonObject);
1156        }
1157
1158        /**
1159         * Gets the operation.
1160         * @return the operation
1161         */
1162        public Operation getOp() {
1163            return this.op;
1164        }
1165
1166        /**
1167         * Gets the data associated with the operation.
1168         * @return the field object representing the data
1169         */
1170        public Field getData() {
1171            return this.data;
1172        }
1173
1174        /**
1175         * Gets the field key.
1176         * @return the field key
1177         */
1178        public String getFieldKey() {
1179            return this.fieldKey;
1180        }
1181
1182        /**
1183         * Gets the list of field keys.
1184         * @return the list of Strings
1185         */
1186        public List<String> getFieldKeys() {
1187            return this.fieldKeys;
1188        }
1189
1190        /**
1191         * Gets the list of keys of the Enum options.
1192         * @return the list of Strings
1193         */
1194        public List<String> getEnumOptionKeys() {
1195            return this.enumOptionKeys;
1196        }
1197
1198        /**
1199         * Sets the operation.
1200         * @param op the operation
1201         */
1202        public void setOp(Operation op) {
1203            this.op = op;
1204        }
1205
1206        /**
1207         * Sets the data.
1208         * @param data the Field object representing the data
1209         */
1210        public void setData(Field data) {
1211            this.data = data;
1212        }
1213
1214        /**
1215         * Sets the field key.
1216         * @param fieldKey the key of the field
1217         */
1218        public void setFieldKey(String fieldKey) {
1219            this.fieldKey = fieldKey;
1220        }
1221
1222        /**
1223         * Sets the list of the field keys.
1224         * @param fieldKeys the list of strings
1225         */
1226        public void setFieldKeys(List<String> fieldKeys) {
1227            this.fieldKeys = fieldKeys;
1228        }
1229
1230        /**
1231         * Sets the list of the enum option keys.
1232         * @param enumOptionKeys the list of Strings
1233         */
1234        public void setEnumOptionKeys(List<String> enumOptionKeys) {
1235            this.enumOptionKeys = enumOptionKeys;
1236        }
1237
1238        /**
1239         * Gets the enum option key.
1240         * @return the enum option key
1241         */
1242        public String getEnumOptionKey() {
1243            return this.enumOptionKey;
1244        }
1245
1246        /**
1247         * Sets the enum option key.
1248         * @param enumOptionKey the enum option key
1249         */
1250        public void setEnumOptionKey(String enumOptionKey) {
1251            this.enumOptionKey = enumOptionKey;
1252        }
1253
1254        /**
1255         * Gets the multi-select option key.
1256         * @return the key.
1257         */
1258        public String getMultiSelectOptionKey() {
1259            return this.multiSelectOptionKey;
1260        }
1261
1262        /**
1263         * Sets the multi-select option key.
1264         * @param key the key.
1265         */
1266        public void setMultiSelectOptionKey(String key) {
1267            this.multiSelectOptionKey = key;
1268        }
1269
1270        /**
1271         * Gets the list of multiselect option keys.
1272         * @return the list of keys.
1273         */
1274        public List<String> getMultiSelectOptionKeys() {
1275            return this.multiSelectOptionKeys;
1276        }
1277
1278        /**
1279         * Sets the multi-select option keys.
1280         * @param keys the list of keys.
1281         */
1282        public void setMultiSelectOptionKeys(List<String> keys) {
1283            this.multiSelectOptionKeys = keys;
1284        }
1285
1286        /**
1287         * {@inheritDoc}
1288         */
1289        @Override
1290        void parseJSONMember(JsonObject.Member member) {
1291            JsonValue value = member.getValue();
1292            String memberName = member.getName();
1293            if (memberName.equals("op")) {
1294                this.op = Operation.valueOf(value.asString());
1295            } else if (memberName.equals("data")) {
1296                this.data = new Field(value.asObject());
1297            } else if (memberName.equals("fieldKey")) {
1298                this.fieldKey = value.asString();
1299            } else if (memberName.equals("fieldKeys")) {
1300                if (this.fieldKeys == null) {
1301                    this.fieldKeys = new ArrayList<String>();
1302                } else {
1303                    this.fieldKeys.clear();
1304                }
1305
1306                JsonArray array = value.asArray();
1307                for (JsonValue jsonValue: array) {
1308                    this.fieldKeys.add(jsonValue.asString());
1309                }
1310            } else if (memberName.equals("enumOptionKeys")) {
1311                if (this.enumOptionKeys == null) {
1312                    this.enumOptionKeys = new ArrayList<String>();
1313                } else {
1314                    this.enumOptionKeys.clear();
1315                }
1316
1317                JsonArray array = value.asArray();
1318                for (JsonValue jsonValue: array) {
1319                    this.enumOptionKeys.add(jsonValue.asString());
1320                }
1321            } else if (memberName.equals("enumOptionKey")) {
1322                this.enumOptionKey = value.asString();
1323            } else if (memberName.equals("multiSelectOptionKey")) {
1324                this.multiSelectOptionKey = value.asString();
1325            } else if (memberName.equals("multiSelectOptionKeys")) {
1326                this.multiSelectOptionKeys = new ArrayList<String>();
1327                for (JsonValue key : value.asArray()) {
1328                    this.multiSelectOptionKeys.add(key.asString());
1329                }
1330            }
1331        }
1332    }
1333
1334    /**
1335     * Possible template operations.
1336     */
1337    public enum Operation {
1338
1339        /**
1340         * Adds an enum option at the end of the enum option list for the specified field.
1341         */
1342        addEnumOption,
1343
1344        /**
1345         * Edits the enum option.
1346         */
1347        editEnumOption,
1348
1349        /**
1350         * Removes the specified enum option from the specified enum field.
1351         */
1352        removeEnumOption,
1353
1354        /**
1355         * Adds a field at the end of the field list for the template.
1356         */
1357        addField,
1358
1359        /**
1360         * Edits any number of the base properties of a field: displayName, hidden, description.
1361         */
1362        editField,
1363
1364        /**
1365         * Removes the specified field from the template.
1366         */
1367        removeField,
1368
1369        /**
1370         * Edits any number of the base properties of a template: displayName, hidden.
1371         */
1372        editTemplate,
1373
1374        /**
1375         * Reorders the enum option list to match the requested enum option list.
1376         */
1377        reorderEnumOptions,
1378
1379        /**
1380         * Reorders the field list to match the requested field list.
1381         */
1382        reorderFields,
1383
1384        /**
1385         * Adds a new option to a multiselect field.
1386         */
1387        addMultiSelectOption,
1388
1389        /**
1390         * Edits an existing option in a multiselect field.
1391         */
1392        editMultiSelectOption,
1393
1394        /**
1395         * Removes an option from a multiselect field.
1396         */
1397        removeMultiSelectOption,
1398
1399        /**
1400         * Changes the display order of options in a multiselect field.
1401         */
1402        reorderMultiSelectOptions
1403    }
1404}