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