001package com.box.sdk; 002 003import java.net.URL; 004import java.util.ArrayList; 005import java.util.List; 006 007import com.eclipsesource.json.JsonArray; 008import com.eclipsesource.json.JsonObject; 009import com.eclipsesource.json.JsonValue; 010 011 012/** 013 * The MetadataTemplate class represents the Box metadata template object. 014 * Templates allow the metadata service to provide a multitude of services, 015 * such as pre-defining sets of key:value pairs or schema enforcement on specific fields. 016 * 017 * @see <a href="https://docs.box.com/reference#metadata-templates">Box metadata templates</a> 018 */ 019public class MetadataTemplate extends BoxJSONObject { 020 021 /** 022 * @see #getMetadataTemplate(BoxAPIConnection) 023 */ 024 private static final URLTemplate METADATA_TEMPLATE_URL_TEMPLATE 025 = new URLTemplate("metadata_templates/%s/%s/schema"); 026 027 /** 028 * @see #createMetadataTemplate(BoxAPIConnection, String, String, String, boolean, List) 029 */ 030 private static final URLTemplate METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE 031 = new URLTemplate("metadata_templates/schema"); 032 033 /** 034 * @see #getEnterpriseMetadataTemplates(String, int, BoxAPIConnection, String...) 035 */ 036 private static final URLTemplate ENTERPRISE_METADATA_URL_TEMPLATE = new URLTemplate("metadata_templates/%s"); 037 038 /** 039 * Default metadata type to be used in query. 040 */ 041 private static final String DEFAULT_METADATA_TYPE = "properties"; 042 043 /** 044 * Global metadata scope. Used by default if the metadata type is "properties". 045 */ 046 private static final String GLOBAL_METADATA_SCOPE = "global"; 047 048 /** 049 * Enterprise metadata scope. Used by default if the metadata type is not "properties". 050 */ 051 private static final String ENTERPRISE_METADATA_SCOPE = "enterprise"; 052 053 /** 054 * Default number of entries per page. 055 */ 056 private static final int DEFAULT_ENTRIES_LIMIT = 100; 057 058 /** 059 * @see #getTemplateKey() 060 */ 061 private String templateKey; 062 063 /** 064 * @see #getScope() 065 */ 066 private String scope; 067 068 /** 069 * @see #getDisplayName() 070 */ 071 private String displayName; 072 073 /** 074 * @see #getIsHidden() 075 */ 076 private Boolean isHidden; 077 078 /** 079 * @see #getFields() 080 */ 081 private List<Field> fields; 082 083 /** 084 * Constructs an empty metadata template. 085 */ 086 public MetadataTemplate() { 087 super(); 088 } 089 090 /** 091 * Constructs a metadata template from a JSON string. 092 * @param json the json encoded metadate template. 093 */ 094 public MetadataTemplate(String json) { 095 super(json); 096 } 097 098 /** 099 * Constructs a metadate template from a JSON object. 100 * @param jsonObject the json encoded metadate template. 101 */ 102 MetadataTemplate(JsonObject jsonObject) { 103 super(jsonObject); 104 } 105 106 /** 107 * Gets the unique template key to identify the metadata template. 108 * @return the unique template key to identify the metadata template. 109 */ 110 public String getTemplateKey() { 111 return this.templateKey; 112 } 113 114 /** 115 * Gets the metadata template scope. 116 * @return the metadata template scope. 117 */ 118 public String getScope() { 119 return this.scope; 120 } 121 122 /** 123 * Gets the displayed metadata template name. 124 * @return the displayed metadata template name. 125 */ 126 public String getDisplayName() { 127 return this.displayName; 128 } 129 130 /** 131 * Gets is the metadata template hidden. 132 * @return is the metadata template hidden. 133 */ 134 public Boolean getIsHidden() { 135 return this.isHidden; 136 } 137 138 /** 139 * Gets the iterable with all fields the metadata template contains. 140 * @return the iterable with all fields the metadata template contains. 141 */ 142 public List<Field> getFields() { 143 return this.fields; 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 void parseJSONMember(JsonObject.Member member) { 151 JsonValue value = member.getValue(); 152 String memberName = member.getName(); 153 if (memberName.equals("templateKey")) { 154 this.templateKey = value.asString(); 155 } else if (memberName.equals("scope")) { 156 this.scope = value.asString(); 157 } else if (memberName.equals("displayName")) { 158 this.displayName = value.asString(); 159 } else if (memberName.equals("hidden")) { 160 this.isHidden = value.asBoolean(); 161 } else if (memberName.equals("fields")) { 162 this.fields = new ArrayList<Field>(); 163 for (JsonValue field: value.asArray()) { 164 this.fields.add(new Field(field.asObject())); 165 } 166 } 167 } 168 169 /** 170 * Creates new metadata template. 171 * @param api the API connection to be used. 172 * @param scope the scope of the object. 173 * @param templateKey a unique identifier for the template. 174 * @param displayName the display name of the field. 175 * @param hidden whether this template is hidden in the UI. 176 * @param fields the ordered set of fields for the template 177 * @return the metadata template returned from the server. 178 */ 179 public static MetadataTemplate createMetadataTemplate(BoxAPIConnection api, String scope, String templateKey, 180 String displayName, boolean hidden, List<Field> fields) { 181 182 JsonObject jsonObject = new JsonObject(); 183 jsonObject.add("scope", scope); 184 jsonObject.add("displayName", displayName); 185 jsonObject.add("hidden", hidden); 186 187 if (templateKey != null) { 188 jsonObject.add("templateKey", templateKey); 189 } 190 191 JsonArray fieldsArray = new JsonArray(); 192 if (fields != null && !fields.isEmpty()) { 193 for (Field field : fields) { 194 JsonObject fieldObj = getFieldJsonObject(field); 195 196 fieldsArray.add(fieldObj); 197 } 198 199 jsonObject.add("fields", fieldsArray); 200 } 201 202 URL url = METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE.build(api.getBaseURL()); 203 BoxJSONRequest request = new BoxJSONRequest(api, url, "POST"); 204 request.setBody(jsonObject.toString()); 205 206 BoxJSONResponse response = (BoxJSONResponse) request.send(); 207 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 208 209 return new MetadataTemplate(responseJSON); 210 } 211 212 /** 213 * Gets the JsonObject representation of the given field object. 214 * @param field represents a template field 215 * @return the json object 216 */ 217 private static JsonObject getFieldJsonObject(Field field) { 218 JsonObject fieldObj = new JsonObject(); 219 fieldObj.add("type", field.getType()); 220 fieldObj.add("key", field.getKey()); 221 fieldObj.add("displayName", field.getDisplayName()); 222 223 String fieldDesc = field.getDescription(); 224 if (fieldDesc != null) { 225 fieldObj.add("description", field.getDescription()); 226 } 227 228 Boolean fieldIsHidden = field.getIsHidden(); 229 if (fieldIsHidden != null) { 230 fieldObj.add("hidden", field.getIsHidden()); 231 } 232 233 JsonArray array = new JsonArray(); 234 List<String> options = field.getOptions(); 235 if (options != null && !options.isEmpty()) { 236 for (String option : options) { 237 JsonObject optionObj = new JsonObject(); 238 optionObj.add("key", option); 239 240 array.add(optionObj); 241 } 242 fieldObj.add("options", array); 243 } 244 245 return fieldObj; 246 } 247 248 /** 249 * Updates the schema of an existing metadata template. 250 * 251 * @param api the API connection to be used 252 * @param scope the scope of the object 253 * @param template Unique identifier of the template 254 * @param fieldOperations the fields that needs to be updated / added in the template 255 * @return the updated metadata template 256 */ 257 public static MetadataTemplate updateMetadataTemplate(BoxAPIConnection api, String scope, String template, 258 List<FieldOperation> fieldOperations) { 259 260 JsonArray array = new JsonArray(); 261 262 for (FieldOperation fieldOperation : fieldOperations) { 263 JsonObject jsonObject = getFieldOperationJsonObject(fieldOperation); 264 array.add(jsonObject); 265 } 266 267 QueryStringBuilder builder = new QueryStringBuilder(); 268 URL url = METADATA_TEMPLATE_URL_TEMPLATE.build(api.getBaseURL(), scope, template); 269 BoxJSONRequest request = new BoxJSONRequest(api, url, "PUT"); 270 request.setBody(array.toString()); 271 272 BoxJSONResponse response = (BoxJSONResponse) request.send(); 273 JsonObject responseJson = JsonObject.readFrom(response.getJSON()); 274 275 return new MetadataTemplate(responseJson); 276 } 277 278 /** 279 * Gets the JsonObject representation of the Field Operation. 280 * @param fieldOperation represents the template update operation 281 * @return the json object 282 */ 283 private static JsonObject getFieldOperationJsonObject(FieldOperation fieldOperation) { 284 JsonObject jsonObject = new JsonObject(); 285 jsonObject.add("op", fieldOperation.getOp().toString()); 286 287 String fieldKey = fieldOperation.getFieldKey(); 288 if (fieldKey != null) { 289 jsonObject.add("fieldKey", fieldKey); 290 } 291 292 Field field = fieldOperation.getData(); 293 if (field != null) { 294 JsonObject fieldObj = new JsonObject(); 295 296 String type = field.getType(); 297 if (type != null) { 298 fieldObj.add("type", type); 299 } 300 301 String key = field.getKey(); 302 if (key != null) { 303 fieldObj.add("key", key); 304 } 305 306 String displayName = field.getDisplayName(); 307 if (displayName != null) { 308 fieldObj.add("displayName", displayName); 309 } 310 311 String description = field.getDescription(); 312 if (description != null) { 313 fieldObj.add("description", description); 314 } 315 316 Boolean hidden = field.getIsHidden(); 317 if (hidden != null) { 318 fieldObj.add("hidden", hidden); 319 } 320 321 List<String> options = field.getOptions(); 322 if (options != null) { 323 JsonArray array = new JsonArray(); 324 for (String option: options) { 325 JsonObject optionObj = new JsonObject(); 326 optionObj.add("key", option); 327 328 array.add(optionObj); 329 } 330 331 fieldObj.add("options", array); 332 } 333 334 jsonObject.add("data", fieldObj); 335 } 336 337 List<String> fieldKeys = fieldOperation.getFieldKeys(); 338 if (fieldKeys != null) { 339 jsonObject.add("fieldKeys", getJsonArray(fieldKeys)); 340 } 341 342 List<String> enumOptionKeys = fieldOperation.getEnumOptionKeys(); 343 if (enumOptionKeys != null) { 344 jsonObject.add("enumOptionKeys", getJsonArray(enumOptionKeys)); 345 } 346 347 return jsonObject; 348 } 349 350 /** 351 * Gets the Json Array representation of the given list of strings. 352 * @param keys List of strings 353 * @return the JsonArray represents the list of keys 354 */ 355 private static JsonArray getJsonArray(List<String> keys) { 356 JsonArray array = new JsonArray(); 357 for (String key : keys) { 358 array.add(key); 359 } 360 361 return array; 362 } 363 364 /** 365 * Gets the metadata template of properties. 366 * @param api the API connection to be used. 367 * @return the metadata template returned from the server. 368 */ 369 public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api) { 370 return getMetadataTemplate(api, DEFAULT_METADATA_TYPE); 371 } 372 373 /** 374 * Gets the metadata template of specified template type. 375 * @param api the API connection to be used. 376 * @param templateName the metadata template type name. 377 * @return the metadata template returned from the server. 378 */ 379 public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api, String templateName) { 380 String scope = scopeBasedOnType(templateName); 381 return getMetadataTemplate(api, templateName, scope); 382 } 383 384 /** 385 * Gets the metadata template of specified template type. 386 * @param api the API connection to be used. 387 * @param templateName the metadata template type name. 388 * @param scope the metadata template scope (global or enterprise). 389 * @param fields the fields to retrieve. 390 * @return the metadata template returned from the server. 391 */ 392 public static MetadataTemplate getMetadataTemplate( 393 BoxAPIConnection api, String templateName, String scope, String ... fields) { 394 QueryStringBuilder builder = new QueryStringBuilder(); 395 if (fields.length > 0) { 396 builder.appendParam("fields", fields); 397 } 398 URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildWithQuery( 399 api.getBaseURL(), builder.toString(), scope, templateName); 400 BoxAPIRequest request = new BoxAPIRequest(api, url, "GET"); 401 BoxJSONResponse response = (BoxJSONResponse) request.send(); 402 return new MetadataTemplate(response.getJSON()); 403 } 404 405 /** 406 * Returns all metadata templates within a user's enterprise. 407 * @param api the API connection to be used. 408 * @param fields the fields to retrieve. 409 * @return the metadata template returned from the server. 410 */ 411 public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(BoxAPIConnection api, String ... fields) { 412 return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, api, fields); 413 } 414 415 /** 416 * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported. 417 * @param scope the scope of the metadata templates. 418 * @param api the API connection to be used. 419 * @param fields the fields to retrieve. 420 * @return the metadata template returned from the server. 421 */ 422 public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates( 423 String scope, BoxAPIConnection api, String ... fields) { 424 return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, DEFAULT_ENTRIES_LIMIT, api, fields); 425 } 426 427 /** 428 * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported. 429 * @param scope the scope of the metadata templates. 430 * @param limit maximum number of entries per response. 431 * @param api the API connection to be used. 432 * @param fields the fields to retrieve. 433 * @return the metadata template returned from the server. 434 */ 435 public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates( 436 String scope, int limit, BoxAPIConnection api, String ... fields) { 437 QueryStringBuilder builder = new QueryStringBuilder(); 438 if (fields.length > 0) { 439 builder.appendParam("fields", fields); 440 } 441 return new BoxResourceIterable<MetadataTemplate>( 442 api, ENTERPRISE_METADATA_URL_TEMPLATE.buildWithQuery( 443 api.getBaseURL(), builder.toString(), scope), limit) { 444 445 @Override 446 protected MetadataTemplate factory(JsonObject jsonObject) { 447 return new MetadataTemplate(jsonObject); 448 } 449 }; 450 } 451 452 /** 453 * Determines the metadata scope based on type. 454 * @param typeName type of the metadata. 455 * @return scope of the metadata. 456 */ 457 private static String scopeBasedOnType(String typeName) { 458 return typeName.equals(DEFAULT_METADATA_TYPE) ? GLOBAL_METADATA_SCOPE : ENTERPRISE_METADATA_SCOPE; 459 } 460 461 /** 462 * Class contains information about the metadata template field. 463 */ 464 public static class Field extends BoxJSONObject { 465 466 /** 467 * @see #getType() 468 */ 469 private String type; 470 471 /** 472 * @see #getKey() 473 */ 474 private String key; 475 476 /** 477 * @see #getDisplayName() 478 */ 479 private String displayName; 480 481 /** 482 * @see #getIsHidden() 483 */ 484 private Boolean isHidden; 485 486 /** 487 * @see #getDescription() 488 */ 489 private String description; 490 491 /** 492 * @see #getOptions() 493 */ 494 private List<String> options; 495 496 /** 497 * Constructs an empty metadata template. 498 */ 499 public Field() { 500 super(); 501 } 502 503 /** 504 * Constructs a metadate template field from a JSON string. 505 * @param json the json encoded metadate template field. 506 */ 507 public Field(String json) { 508 super(json); 509 } 510 511 /** 512 * Constructs a metadate template field from a JSON object. 513 * @param jsonObject the json encoded metadate template field. 514 */ 515 Field(JsonObject jsonObject) { 516 super(jsonObject); 517 } 518 519 /** 520 * Gets the data type of the field's value. 521 * @return the data type of the field's value. 522 */ 523 public String getType() { 524 return this.type; 525 } 526 527 /** 528 * Sets the data type of the field's value. 529 * @param type the data type of the field's value. 530 */ 531 public void setType(String type) { 532 this.type = type; 533 } 534 535 /** 536 * Gets the key of the field. 537 * @return the key of the field. 538 */ 539 public String getKey() { 540 return this.key; 541 } 542 543 /** 544 * Sets the key of the field. 545 * @param key the key of the field. 546 */ 547 public void setKey(String key) { 548 this.key = key; 549 } 550 551 /** 552 * Gets the display name of the field. 553 * @return the display name of the field. 554 */ 555 public String getDisplayName() { 556 return this.displayName; 557 } 558 559 /** 560 * Sets the display name of the field. 561 * @param displayName the display name of the field. 562 */ 563 public void setDisplayName(String displayName) { 564 this.displayName = displayName; 565 } 566 567 /** 568 * Gets is metadata template field hidden. 569 * @return is metadata template field hidden. 570 */ 571 public Boolean getIsHidden() { 572 return this.isHidden; 573 } 574 575 /** 576 * Sets is metadata template field hidden. 577 * @param isHidden is metadata template field hidden? 578 */ 579 public void setIsHidden(boolean isHidden) { 580 this.isHidden = isHidden; 581 } 582 583 /** 584 * Gets the description of the field. 585 * @return the description of the field. 586 */ 587 public String getDescription() { 588 return this.description; 589 } 590 591 /** 592 * Sets the description of the field. 593 * @param description the description of the field. 594 */ 595 public void setDescription(String description) { 596 this.description = description; 597 } 598 599 /** 600 * Gets list of possible options for enum type of the field. 601 * @return list of possible options for enum type of the field. 602 */ 603 public List<String> getOptions() { 604 return this.options; 605 } 606 607 /** 608 * Sets list of possible options for enum type of the field. 609 * @param options list of possible options for enum type of the field. 610 */ 611 public void setOptions(List<String> options) { 612 this.options = options; 613 } 614 615 /** 616 * {@inheritDoc} 617 */ 618 @Override 619 void parseJSONMember(JsonObject.Member member) { 620 JsonValue value = member.getValue(); 621 String memberName = member.getName(); 622 if (memberName.equals("type")) { 623 this.type = value.asString(); 624 } else if (memberName.equals("key")) { 625 this.key = value.asString(); 626 } else if (memberName.equals("displayName")) { 627 this.displayName = value.asString(); 628 } else if (memberName.equals("hidden")) { 629 this.isHidden = value.asBoolean(); 630 } else if (memberName.equals("description")) { 631 this.description = value.asString(); 632 } else if (memberName.equals("options")) { 633 this.options = new ArrayList<String>(); 634 for (JsonValue key: value.asArray()) { 635 this.options.add(key.asObject().get("key").asString()); 636 } 637 } 638 } 639 } 640 641 /** 642 * Posssible operations that can be performed in a Metadata template. 643 * <ul> 644 * <li>Add an enum option</li> 645 * <li>Add a field</li> 646 * <li>Edit a field</li> 647 * <li>Edit template</li> 648 * <li>Reorder the enum option</li> 649 * <li>Reorder the field list</li> 650 * </ul> 651 */ 652 public static class FieldOperation extends BoxJSONObject { 653 654 private Operation op; 655 private Field data; 656 private String fieldKey; 657 private List<String> fieldKeys; 658 private List<String> enumOptionKeys; 659 660 /** 661 * Constructs an empty FieldOperation. 662 */ 663 public FieldOperation() { 664 super(); 665 } 666 667 /** 668 * Constructs a Field operation from a JSON string. 669 * @param json the json encoded metadate template field. 670 */ 671 public FieldOperation(String json) { 672 super(json); 673 } 674 675 /** 676 * Constructs a Field operation from a JSON object. 677 * @param jsonObject the json encoded metadate template field. 678 */ 679 FieldOperation(JsonObject jsonObject) { 680 super(jsonObject); 681 } 682 683 /** 684 * Gets the operation. 685 * @return the operation 686 */ 687 public Operation getOp() { 688 return this.op; 689 } 690 691 /** 692 * Gets the data associated with the operation. 693 * @return the field object representing the data 694 */ 695 public Field getData() { 696 return this.data; 697 } 698 699 /** 700 * Gets the field key. 701 * @return the field key 702 */ 703 public String getFieldKey() { 704 return this.fieldKey; 705 } 706 707 /** 708 * Gets the list of field keys. 709 * @return the list of Strings 710 */ 711 public List<String> getFieldKeys() { 712 return this.fieldKeys; 713 } 714 715 /** 716 * Gets the list of keys of the Enum options. 717 * @return the list of Strings 718 */ 719 public List<String> getEnumOptionKeys() { 720 return this.enumOptionKeys; 721 } 722 723 /** 724 * Sets the operation. 725 * @param op the operation 726 */ 727 public void setOp(Operation op) { 728 this.op = op; 729 } 730 731 /** 732 * Sets the data. 733 * @param data the Field object representing the data 734 */ 735 public void setData(Field data) { 736 this.data = data; 737 } 738 739 /** 740 * Sets the field key. 741 * @param fieldKey the key of the field 742 */ 743 public void setFieldKey(String fieldKey) { 744 this.fieldKey = fieldKey; 745 } 746 747 /** 748 * Sets the list of the field keys. 749 * @param fieldKeys the list of strings 750 */ 751 public void setFieldKeys(List<String> fieldKeys) { 752 this.fieldKeys = fieldKeys; 753 } 754 755 /** 756 * Sets the list of the enum option keys. 757 * @param enumOptionKeys the list of Strings 758 */ 759 public void setEnumOptionKeys(List<String> enumOptionKeys) { 760 this.enumOptionKeys = enumOptionKeys; 761 } 762 763 /** 764 * {@inheritDoc} 765 */ 766 @Override 767 void parseJSONMember(JsonObject.Member member) { 768 JsonValue value = member.getValue(); 769 String memberName = member.getName(); 770 if (memberName.equals("op")) { 771 this.op = Operation.valueOf(value.asString()); 772 } else if (memberName.equals("data")) { 773 this.data = new Field(value.asObject()); 774 } else if (memberName.equals("fieldKey")) { 775 this.fieldKey = value.asString(); 776 } else if (memberName.equals("fieldKeys")) { 777 if (this.fieldKeys == null) { 778 this.fieldKeys = new ArrayList<String>(); 779 } else { 780 this.fieldKeys.clear(); 781 } 782 783 JsonArray array = value.asArray(); 784 for (JsonValue jsonValue: array) { 785 this.fieldKeys.add(jsonValue.asString()); 786 } 787 } else if (memberName.equals("enumOptionKeys")) { 788 if (this.enumOptionKeys == null) { 789 this.enumOptionKeys = new ArrayList<String>(); 790 } else { 791 this.enumOptionKeys.clear(); 792 } 793 794 JsonArray array = value.asArray(); 795 for (JsonValue jsonValue: array) { 796 this.enumOptionKeys.add(jsonValue.asString()); 797 } 798 } 799 } 800 } 801 802 /** 803 * Possible template operations. 804 */ 805 public enum Operation { 806 807 /** 808 * Adds an enum option at the end of the enum option list for the specified field. 809 */ 810 addEnumOption, 811 812 /** 813 * Adds a field at the end of the field list for the template. 814 */ 815 addField, 816 817 /** 818 * Edits any number of the base properties of a field: displayName, hidden, description. 819 */ 820 editField, 821 822 /** 823 * Edits any number of the base properties of a template: displayName, hidden. 824 */ 825 editTemplate, 826 827 /** 828 * Reorders the enum option list to match the requested enum option list. 829 */ 830 reorderEnumOptions, 831 832 /** 833 * Reorders the field list to match the requested field list. 834 */ 835 reorderFields 836 } 837}