• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

box / box-java-sdk / #4292

16 Dec 2024 09:34AM UTC coverage: 71.866% (+0.1%) from 71.724%
#4292

push

github

web-flow
feat: Support downloading file from shared link (#1282)


---------

Co-authored-by: Minh Nguyen Cong <mcong@box.com>

19 of 23 new or added lines in 2 files covered. (82.61%)

1 existing line in 1 file now uncovered.

8105 of 11278 relevant lines covered (71.87%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.73
/src/main/java/com/box/sdk/BoxItem.java
1
package com.box.sdk;
2

3
import com.eclipsesource.json.Json;
4
import com.eclipsesource.json.JsonArray;
5
import com.eclipsesource.json.JsonObject;
6
import com.eclipsesource.json.JsonValue;
7
import java.net.URL;
8
import java.util.ArrayList;
9
import java.util.Date;
10
import java.util.HashSet;
11
import java.util.List;
12
import java.util.Set;
13

14
/**
15
 * The abstract base class for items in a user's file tree (files, folders, etc.).
16
 */
17
public abstract class BoxItem extends BoxResource {
18
    /**
19
     * An array of all possible file fields that can be requested when calling {@link #getInfo(String...)}.
20
     */
21
    public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", "description",
1✔
22
        "size", "path_collection", "created_at", "modified_at", "trashed_at", "purged_at", "content_created_at",
23
        "content_modified_at", "created_by", "modified_by", "owned_by", "shared_link", "parent", "item_status",
24
        "version_number", "comment_count", "permissions", "tags", "lock", "extension", "is_package",
25
        "folder_upload_email", "item_collection", "sync_state", "has_collaborations", "can_non_owners_invite",
26
        "file_version", "collections", "expires_at"};
27
    /**
28
     * Shared Item URL Template.
29
     */
30
    public static final URLTemplate SHARED_ITEM_URL_TEMPLATE = new URLTemplate("shared_items");
1✔
31

32
    /**
33
     * Url template for operations with watermarks.
34
     */
35
    public static final URLTemplate WATERMARK_URL_TEMPLATE = new URLTemplate("/watermark");
1✔
36

37
    /**
38
     * Constructs a BoxItem for an item with a given ID.
39
     *
40
     * @param api the API connection to be used by the item.
41
     * @param id  the ID of the item.
42
     */
43
    public BoxItem(BoxAPIConnection api, String id) {
44
        super(api, id);
1✔
45
    }
1✔
46

47
    /**
48
     * Gets an item that was shared with a shared link.
49
     *
50
     * @param api        the API connection to be used by the shared item.
51
     * @param sharedLink the shared link to the item.
52
     * @return info about the shared item.
53
     */
54
    public static BoxItem.Info getSharedItem(BoxAPIConnection api, String sharedLink) {
55
        return getSharedItem(api, sharedLink, null);
1✔
56
    }
57

58
    /**
59
     * Gets an item that was shared with a password-protected shared link.
60
     *
61
     * @param api        the API connection to be used by the shared item.
62
     * @param sharedLink the shared link to the item.
63
     * @param password   the password for the shared link. Use `null` if shared link has no password.
64
     * @param fields     the fields to retrieve.
65
     * @return info about the shared item.
66
     */
67
    public static BoxItem.Info getSharedItem(
68
            BoxAPIConnection api, String sharedLink, String password, String... fields
69
    ) {
70
        QueryStringBuilder builder = new QueryStringBuilder();
1✔
71
        if (fields.length > 0) {
1✔
72
            builder.appendParam("fields", fields);
1✔
73
        }
74
        URL url = SHARED_ITEM_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
1✔
75
        BoxJSONRequest request = new BoxJSONRequest(api, url, "GET");
1✔
76

77
        request.addHeader("BoxApi", BoxSharedLink.getSharedLinkHeaderValue(sharedLink, password));
1✔
78

79
        try (BoxJSONResponse response = request.send()) {
1✔
80
            JsonObject json = Json.parse(response.getJSON()).asObject();
1✔
81
            return (BoxItem.Info) BoxResource.parseInfo(api, json);
1✔
82
        }
83
    }
84

85
    /**
86
     * @return URL for the current object, constructed as base URL pus an item specifier.
87
     */
88
    protected URL getItemURL() {
89
        return new URLTemplate("").build(this.getAPI().getBaseURL());
×
90
    }
91

92
    /**
93
     * Used to retrieve the watermark for the item.
94
     * If the item does not have a watermark applied to it, a 404 Not Found will be returned by API.
95
     *
96
     * @param itemUrl url template for the item.
97
     * @param fields  the fields to retrieve.
98
     * @return the watermark associated with the item.
99
     */
100
    protected BoxWatermark getWatermark(URLTemplate itemUrl, String... fields) {
101
        URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID());
1✔
102
        QueryStringBuilder builder = new QueryStringBuilder();
1✔
103
        if (fields.length > 0) {
1✔
104
            builder.appendParam("fields", fields);
×
105
        }
106
        URL url = WATERMARK_URL_TEMPLATE.buildWithQuery(watermarkUrl.toString(), builder.toString());
1✔
107
        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
1✔
108
        try (BoxJSONResponse response = request.send()) {
1✔
109
            return new BoxWatermark(response.getJSON());
1✔
110
        }
111
    }
112

113
    /**
114
     * Used to apply or update the watermark for the item.
115
     *
116
     * @param itemUrl url template for the item.
117
     * @param imprint the value must be "default", as custom watermarks is not yet supported.
118
     * @return the watermark associated with the item.
119
     */
120
    protected BoxWatermark applyWatermark(URLTemplate itemUrl, String imprint) {
121
        URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID());
1✔
122
        URL url = WATERMARK_URL_TEMPLATE.build(watermarkUrl.toString());
1✔
123
        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
1✔
124
        JsonObject body = new JsonObject()
1✔
125
            .add(BoxWatermark.WATERMARK_JSON_KEY, new JsonObject()
1✔
126
                .add(BoxWatermark.WATERMARK_IMPRINT_JSON_KEY, imprint));
1✔
127
        request.setBody(body.toString());
1✔
128
        try (BoxJSONResponse response = request.send()) {
1✔
129
            return new BoxWatermark(response.getJSON());
1✔
130
        }
131
    }
132

133
    /**
134
     * Removes a watermark from the item.
135
     * If the item did not have a watermark applied to it, a 404 Not Found will be returned by API.
136
     *
137
     * @param itemUrl url template for the item.
138
     */
139
    protected void removeWatermark(URLTemplate itemUrl) {
140
        URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID());
1✔
141
        URL url = WATERMARK_URL_TEMPLATE.build(watermarkUrl.toString());
1✔
142
        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
1✔
143
        request.send().close();
1✔
144
    }
1✔
145

146
    /**
147
     * Copies this item to another folder.
148
     *
149
     * @param destination the destination folder.
150
     * @return info about the copied item.
151
     */
152
    public abstract BoxItem.Info copy(BoxFolder destination);
153

154
    /**
155
     * Copies this item to another folder and gives it a new name. If the destination is the same folder as the item's
156
     * current parent, then newName must be a new, unique name.
157
     *
158
     * @param destination the destination folder.
159
     * @param newName     a new name for the copied item.
160
     * @return info about the copied item.
161
     */
162
    public abstract BoxItem.Info copy(BoxFolder destination, String newName);
163

164
    /**
165
     * Moves this item to another folder.
166
     *
167
     * @param destination the destination folder.
168
     * @return info about the moved item.
169
     */
170
    public abstract BoxItem.Info move(BoxFolder destination);
171

172
    /**
173
     * Moves this item to another folder and gives it a new name.
174
     *
175
     * @param destination the destination folder.
176
     * @param newName     a new name for the moved item.
177
     * @return info about the moved item.
178
     */
179
    public abstract BoxItem.Info move(BoxFolder destination, String newName);
180

181
    /**
182
     * Gets information about this item that's limited to a list of specified fields.
183
     *
184
     * @param fields the fields to retrieve.
185
     * @return info about this item containing only the specified fields.
186
     */
187
    public abstract BoxItem.Info getInfo(String... fields);
188

189
    /**
190
     * Sets the collections that this item belongs to.
191
     *
192
     * @param collections the collections that this item should belong to.
193
     * @return info about the item, including the collections it belongs to.
194
     */
195
    public abstract BoxItem.Info setCollections(BoxCollection... collections);
196

197
    /**
198
     * Contains information about a BoxItem.
199
     */
200
    public abstract class Info extends BoxResource.Info {
201
        private String type;
202
        private String sequenceID;
203
        private String etag;
204
        private String name;
205
        private Date createdAt;
206
        private Date modifiedAt;
207
        private String description;
208
        private long size;
209
        private List<BoxFolder.Info> pathCollection;
210
        private BoxUser.Info createdBy;
211
        private BoxUser.Info modifiedBy;
212
        private Date trashedAt;
213
        private Date purgedAt;
214
        private Date contentCreatedAt;
215
        private Date contentModifiedAt;
216
        private BoxUser.Info ownedBy;
217
        private BoxSharedLink sharedLink;
218
        private List<String> tags;
219
        private BoxFolder.Info parent;
220
        private String itemStatus;
221
        private Date expiresAt;
222
        private Set<BoxCollection.Info> collections;
223
        private String downloadUrl;
224

225
        /**
226
         * Constructs an empty Info object.
227
         */
228
        public Info() {
1✔
229
            super();
1✔
230
        }
1✔
231

232
        /**
233
         * Constructs an Info object by parsing information from a JSON string.
234
         *
235
         * @param json the JSON string to parse.
236
         */
237
        public Info(String json) {
1✔
238
            super(json);
1✔
239
        }
1✔
240

241
        /**
242
         * Constructs an Info object using an already parsed JSON object.
243
         *
244
         * @param jsonObject the parsed JSON object.
245
         */
246
        Info(JsonObject jsonObject) {
1✔
247
            super(jsonObject);
1✔
248
        }
1✔
249

250
        /**
251
         * Gets the item type.
252
         *
253
         * @return the item's type.
254
         */
255
        public String getType() {
256
            return this.type;
1✔
257
        }
258

259
        /**
260
         * Gets a unique string identifying the version of the item.
261
         *
262
         * @return a unique string identifying the version of the item.
263
         */
264
        public String getEtag() {
265
            return this.etag;
1✔
266
        }
267

268
        /**
269
         * Gets the name of the item.
270
         *
271
         * @return the name of the item.
272
         */
273
        public String getName() {
274
            return this.name;
1✔
275
        }
276

277
        /**
278
         * Sets the name of the item.
279
         *
280
         * @param name the new name of the item.
281
         */
282
        public void setName(String name) {
283
            this.name = name;
1✔
284
            this.addPendingChange("name", name);
1✔
285
        }
1✔
286

287
        /**
288
         * Gets the time the item was created.
289
         *
290
         * @return the time the item was created.
291
         */
292
        public Date getCreatedAt() {
293
            return this.createdAt;
×
294
        }
295

296
        /**
297
         * Gets the time the item was last modified.
298
         *
299
         * @return the time the item was last modified.
300
         */
301
        public Date getModifiedAt() {
302
            return this.modifiedAt;
×
303
        }
304

305
        /**
306
         * Gets the description of the item.
307
         *
308
         * @return the description of the item.
309
         */
310
        public String getDescription() {
311
            return this.description;
1✔
312
        }
313

314
        /**
315
         * Sets the description of the item.
316
         *
317
         * @param description the new description of the item.
318
         */
319
        public void setDescription(String description) {
320
            this.description = description;
1✔
321
            this.addPendingChange("description", description);
1✔
322
        }
1✔
323

324
        /**
325
         * Gets the size of the item in bytes.
326
         *
327
         * @return the size of the item in bytes.
328
         */
329
        public long getSize() {
330
            return this.size;
1✔
331
        }
332

333
        /**
334
         * Gets the path of folders to the item, starting at the root.
335
         *
336
         * @return the path of folders to the item.
337
         */
338
        public List<BoxFolder.Info> getPathCollection() {
339
            return this.pathCollection;
1✔
340
        }
341

342
        /**
343
         * Gets info about the user who created the item.
344
         *
345
         * @return info about the user who created the item.
346
         */
347
        public BoxUser.Info getCreatedBy() {
348
            return this.createdBy;
1✔
349
        }
350

351
        /**
352
         * Gets info about the user who last modified the item.
353
         *
354
         * @return info about the user who last modified the item.
355
         */
356
        public BoxUser.Info getModifiedBy() {
357
            return this.modifiedBy;
1✔
358
        }
359

360
        /**
361
         * Gets the time that the item was trashed.
362
         *
363
         * @return the time that the item was trashed.
364
         */
365
        public Date getTrashedAt() {
366
            return this.trashedAt;
×
367
        }
368

369
        /**
370
         * Gets the time that the item was purged from the trash.
371
         *
372
         * @return the time that the item was purged from the trash.
373
         */
374
        public Date getPurgedAt() {
375
            return this.purgedAt;
×
376
        }
377

378
        /**
379
         * Gets the time that the item was created according to the uploader.
380
         *
381
         * @return the time that the item was created according to the uploader.
382
         */
383
        public Date getContentCreatedAt() {
384
            return this.contentCreatedAt;
×
385
        }
386

387
        /**
388
         * Gets the time that the item was last modified according to the uploader.
389
         *
390
         * @return the time that the item was last modified according to the uploader.
391
         */
392
        public Date getContentModifiedAt() {
393
            return this.contentModifiedAt;
1✔
394
        }
395

396
        /**
397
         * Gets the expires at time for this item.
398
         *
399
         * @return the time that the item will expire at.
400
         */
401
        public Date getExpiresAt() {
402
            return this.expiresAt;
×
403
        }
404

405
        /**
406
         * Gets info about the user who owns the item.
407
         *
408
         * @return info about the user who owns the item.
409
         */
410
        public BoxUser.Info getOwnedBy() {
411
            return this.ownedBy;
1✔
412
        }
413

414
        /**
415
         * Gets the shared link for the item.
416
         *
417
         * @return the shared link for the item.
418
         */
419
        public BoxSharedLink getSharedLink() {
420
            return this.sharedLink;
1✔
421
        }
422

423
        /**
424
         * Sets a shared link for the item.
425
         *
426
         * @param sharedLink the shared link for the item.
427
         */
428
        public void setSharedLink(BoxSharedLink sharedLink) {
429
            this.removeChildObject("shared_link");
1✔
430
            this.sharedLink = sharedLink;
1✔
431
            this.addChildObject("shared_link", sharedLink);
1✔
432
        }
1✔
433

434
        /**
435
         * Removes the shared link for the item.
436
         */
437
        public void removeSharedLink() {
438
            this.addChildObject("shared_link", null);
1✔
439
        }
1✔
440

441
        /**
442
         * Gets a unique ID for use with the {@link EventStream}.
443
         *
444
         * @return a unique ID for use with the EventStream.
445
         */
446
        public String getSequenceID() {
447
            return this.sequenceID;
×
448
        }
449

450
        /**
451
         * Gets a list of all the tags applied to the item.
452
         *
453
         * <p>Note that this field isn't populated by default and must be specified as a field parameter when getting
454
         * Info about the item.</p>
455
         *
456
         * @return a list of all the tags applied to the item.
457
         */
458
        public List<String> getTags() {
459
            return this.tags;
1✔
460
        }
461

462
        /**
463
         * Sets the tags for an item.
464
         *
465
         * @param tags The new tags for the item.
466
         */
467
        public void setTags(List<String> tags) {
468
            this.tags = tags;
1✔
469
            JsonArray tagsJSON = new JsonArray();
1✔
470
            for (String tag : tags) {
1✔
471
                tagsJSON.add(tag);
1✔
472
            }
1✔
473
            this.addPendingChange("tags", tagsJSON);
1✔
474
        }
1✔
475

476
        /**
477
         * Gets info about the parent folder of the item.
478
         *
479
         * @return info about the parent folder of the item.
480
         */
481
        public BoxFolder.Info getParent() {
482
            return this.parent;
1✔
483
        }
484

485
        /**
486
         * Gets the status of the item.
487
         *
488
         * @return the status of the item.
489
         */
490
        public String getItemStatus() {
491
            return this.itemStatus;
×
492
        }
493

494
        /**
495
         * Gets info about the collections that this item belongs to.
496
         *
497
         * @return info about the collections that this item belongs to.
498
         */
499
        public Iterable<BoxCollection.Info> getCollections() {
500
            return this.collections;
×
501
        }
502

503
        /***
504
         * Gets URL that can be used to download the file.
505
         * @return
506
         */
507
        public String getDownloadUrl() {
NEW
508
            return this.downloadUrl;
×
509
        }
510

511
        /**
512
         * Sets the collections that this item belongs to.
513
         *
514
         * @param collections the new list of collections that this item should belong to.
515
         */
516
        public void setCollections(Iterable<BoxCollection> collections) {
517
            if (this.collections == null) {
×
518
                this.collections = new HashSet<>();
×
519
            } else {
520
                this.collections.clear();
×
521
            }
522

523
            JsonArray jsonArray = new JsonArray();
×
524
            for (BoxCollection collection : collections) {
×
525
                JsonObject jsonObject = new JsonObject();
×
526
                jsonObject.add("id", collection.getID());
×
527
                jsonArray.add(jsonObject);
×
528
                this.collections.add(collection.new Info());
×
529
            }
×
530
            this.addPendingChange("collections", jsonArray);
×
531
        }
×
532

533
        @Override
534
        protected void parseJSONMember(JsonObject.Member member) {
535
            super.parseJSONMember(member);
1✔
536
            JsonValue value = member.getValue();
1✔
537
            String memberName = member.getName();
1✔
538

539
            try {
540
                switch (memberName) {
1✔
541
                    case "sequence_id":
542
                        this.sequenceID = value.asString();
1✔
543
                        break;
1✔
544
                    case "type":
545
                        this.type = value.asString();
1✔
546
                        break;
1✔
547
                    case "etag":
548
                        this.etag = value.asString();
1✔
549
                        break;
1✔
550
                    case "name":
551
                        this.name = value.asString();
1✔
552
                        break;
1✔
553
                    case "created_at":
554
                        this.createdAt = BoxDateFormat.parse(value.asString());
1✔
555
                        break;
1✔
556
                    case "modified_at":
557
                        this.modifiedAt = BoxDateFormat.parse(value.asString());
1✔
558
                        break;
1✔
559
                    case "description":
560
                        this.description = value.asString();
1✔
561
                        break;
1✔
562
                    case "size":
563
                        this.size = Double.valueOf(value.toString()).longValue();
1✔
564
                        break;
1✔
565
                    case "trashed_at":
566
                        this.trashedAt = BoxDateFormat.parse(value.asString());
1✔
567
                        break;
1✔
568
                    case "purged_at":
569
                        this.purgedAt = BoxDateFormat.parse(value.asString());
1✔
570
                        break;
1✔
571
                    case "content_created_at":
572
                        this.contentCreatedAt = BoxDateFormat.parse(value.asString());
1✔
573
                        break;
1✔
574
                    case "content_modified_at":
575
                        this.contentModifiedAt = BoxDateFormat.parse(value.asString());
1✔
576
                        break;
1✔
577
                    case "expires_at":
578
                        this.expiresAt = BoxDateFormat.parse(value.asString());
1✔
579
                        break;
1✔
580
                    case "path_collection":
581
                        this.pathCollection = this.parsePathCollection(value.asObject());
1✔
582
                        break;
1✔
583
                    case "created_by":
584
                        this.createdBy = this.parseUserInfo(value.asObject());
1✔
585
                        break;
1✔
586
                    case "modified_by":
587
                        this.modifiedBy = this.parseUserInfo(value.asObject());
1✔
588
                        break;
1✔
589
                    case "owned_by":
590
                        this.ownedBy = this.parseUserInfo(value.asObject());
1✔
591
                        break;
1✔
592
                    case "shared_link":
593
                        if (this.sharedLink == null) {
1✔
594
                            this.setSharedLink(new BoxSharedLink(value.asObject()));
1✔
595
                        } else {
596
                            this.sharedLink.update(value.asObject());
1✔
597
                        }
598
                        break;
1✔
599
                    case "tags":
600
                        this.tags = this.parseTags(value.asArray());
1✔
601
                        break;
1✔
602
                    case "parent":
603
                        JsonObject parentObject = value.asObject();
1✔
604
                        if (this.parent == null) {
1✔
605
                            String id = parentObject.get("id").asString();
1✔
606
                            BoxFolder parentFolder = new BoxFolder(getAPI(), id);
1✔
607
                            this.parent = parentFolder.new Info(parentObject);
1✔
608
                        } else {
1✔
609
                            this.parent.update(parentObject);
1✔
610
                        }
611
                        break;
1✔
612
                    case "item_status":
613
                        this.itemStatus = value.asString();
1✔
614
                        break;
1✔
615
                    case "collections":
616
                        if (this.collections == null) {
×
617
                            this.collections = new HashSet<>();
×
618
                        } else {
619
                            this.collections.clear();
×
620
                        }
621

622
                        BoxAPIConnection api = getAPI();
×
623
                        JsonArray jsonArray = value.asArray();
×
624
                        for (JsonValue arrayValue : jsonArray) {
×
625
                            JsonObject jsonObject = arrayValue.asObject();
×
626
                            String id = jsonObject.get("id").asString();
×
627
                            BoxCollection collection = new BoxCollection(api, id);
×
628
                            BoxCollection.Info collectionInfo = collection.new Info(jsonObject);
×
629
                            this.collections.add(collectionInfo);
×
630
                        }
×
631
                        break;
×
632
                    case "download_url":
633
                        this.downloadUrl = value.asString();
1✔
634
                        break;
1✔
635
                    default:
636
                        break;
637
                }
638
            } catch (Exception e) {
1✔
639
                throw new BoxDeserializationException(memberName, value.toString(), e);
1✔
640
            }
1✔
641
        }
1✔
642

643
        private List<BoxFolder.Info> parsePathCollection(JsonObject jsonObject) {
644
            int count = jsonObject.get("total_count").asInt();
1✔
645
            List<BoxFolder.Info> pathCollection = new ArrayList<>(count);
1✔
646
            JsonArray entries = jsonObject.get("entries").asArray();
1✔
647
            for (JsonValue value : entries) {
1✔
648
                JsonObject entry = value.asObject();
1✔
649
                String id = entry.get("id").asString();
1✔
650
                BoxFolder folder = new BoxFolder(getAPI(), id);
1✔
651
                pathCollection.add(folder.new Info(entry));
1✔
652
            }
1✔
653

654
            return pathCollection;
1✔
655
        }
656

657
        private BoxUser.Info parseUserInfo(JsonObject jsonObject) {
658
            String userID = jsonObject.get("id").asString();
1✔
659
            BoxUser user = new BoxUser(getAPI(), userID);
1✔
660
            return user.new Info(jsonObject);
1✔
661
        }
662

663
        private List<String> parseTags(JsonArray jsonArray) {
664
            List<String> tags = new ArrayList<>(jsonArray.size());
1✔
665
            for (JsonValue value : jsonArray) {
1✔
666
                tags.add(value.asString());
1✔
667
            }
1✔
668

669
            return tags;
1✔
670
        }
671
    }
672
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc