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