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

smartsheet / smartsheet-java-sdk / #57

29 Jul 2025 04:55PM UTC coverage: 60.803% (+0.2%) from 60.611%
#57

push

github

web-flow
Added new sharing endpoints. Deprecated old sharing endpoints. (#125)

* Added new sharing endpoints.
Deprecated old endpoints.

* Updated to 3.3.0 for deprecation date.

* Removed amp.

* fixed `&`

106 of 157 new or added lines in 9 files covered. (67.52%)

4269 of 7021 relevant lines covered (60.8%)

0.61 hits per line

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

72.55
/src/main/java/com/smartsheet/api/internal/AbstractResources.java
1
/*
2
 * Copyright (C) 2025 Smartsheet
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.smartsheet.api.internal;
18

19
import com.fasterxml.jackson.core.JsonParseException;
20
import com.fasterxml.jackson.databind.JsonMappingException;
21
import com.smartsheet.api.AuthorizationException;
22
import com.smartsheet.api.InvalidRequestException;
23
import com.smartsheet.api.ResourceNotFoundException;
24
import com.smartsheet.api.ServiceUnavailableException;
25
import com.smartsheet.api.SmartsheetException;
26
import com.smartsheet.api.SmartsheetRestException;
27
import com.smartsheet.api.internal.http.HttpEntity;
28
import com.smartsheet.api.internal.http.HttpMethod;
29
import com.smartsheet.api.internal.http.HttpRequest;
30
import com.smartsheet.api.internal.http.HttpResponse;
31
import com.smartsheet.api.internal.json.JSONSerializerException;
32
import com.smartsheet.api.internal.util.StreamUtil;
33
import com.smartsheet.api.internal.util.Util;
34
import com.smartsheet.api.models.Attachment;
35
import com.smartsheet.api.models.CopyOrMoveRowDirective;
36
import com.smartsheet.api.models.CopyOrMoveRowResult;
37
import com.smartsheet.api.models.PagedResult;
38
import com.smartsheet.api.models.Result;
39
import org.apache.http.client.methods.CloseableHttpResponse;
40
import org.apache.http.client.methods.HttpPost;
41
import org.apache.http.entity.ContentType;
42
import org.apache.http.entity.mime.MultipartEntityBuilder;
43
import org.apache.http.impl.client.CloseableHttpClient;
44
import org.apache.http.impl.client.HttpClients;
45
import org.slf4j.Logger;
46
import org.slf4j.LoggerFactory;
47

48
import java.io.ByteArrayInputStream;
49
import java.io.ByteArrayOutputStream;
50
import java.io.IOException;
51
import java.io.InputStream;
52
import java.io.OutputStream;
53
import java.lang.reflect.InvocationTargetException;
54
import java.net.URI;
55
import java.net.URLEncoder;
56
import java.nio.charset.StandardCharsets;
57
import java.util.HashMap;
58
import java.util.List;
59
import java.util.Map;
60

61
/**
62
 * This is the base class of the Smartsheet REST API resources.
63
 * <p>
64
 * Thread Safety: This class is thread safe because it is immutable and the underlying SmartsheetImpl is thread safe.
65
 */
66
public abstract class AbstractResources {
67
    /**
68
     * this system property is used to control the number of characters logged from an API response in logs
69
     */
70
    public static final String PROPERTY_RESPONSE_LOG_CHARS = "Smartsheet.responseLogChars";
71

72
    private static final Logger log = LoggerFactory.getLogger(AbstractResources.class);
1✔
73

74
    /**
75
     * The Constant BUFFER_SIZE.
76
     */
77
    private static final int BUFFER_SIZE = 4098;
78

79
    private static final String JSON_CONTENT_TYPE = "application/json";
80
    private static final String HEADER_CONTENT_TYPE = "Content-Type";
81

82
    /**
83
     * The Enum ErrorCode.
84
     */
85
    public enum ErrorCode {
1✔
86
        BAD_REQUEST(400, InvalidRequestException.class),
1✔
87
        NOT_AUTHORIZED(401, AuthorizationException.class),
1✔
88
        FORBIDDEN(403, AuthorizationException.class),
1✔
89
        NOT_FOUND(404, ResourceNotFoundException.class),
1✔
90
        METHOD_NOT_SUPPORTED(405, InvalidRequestException.class),
1✔
91
        INTERNAL_SERVER_ERROR(500, InvalidRequestException.class),
1✔
92
        SERVICE_UNAVAILABLE(503, ServiceUnavailableException.class);
1✔
93

94
        /**
95
         * The error code.
96
         */
97
        int errorCode;
98

99
        /**
100
         * The Exception class.
101
         */
102
        Class<? extends SmartsheetRestException> exceptionClass;
103

104
        /**
105
         * Instantiates a new error code.
106
         *
107
         * @param errorCode      the error code
108
         * @param exceptionClass the Exception class
109
         */
110
        ErrorCode(int errorCode, Class<? extends SmartsheetRestException> exceptionClass) {
1✔
111
            this.errorCode = errorCode;
1✔
112
            this.exceptionClass = exceptionClass;
1✔
113
        }
1✔
114

115
        /**
116
         * Gets the error code.
117
         *
118
         * @param errorNumber the error number
119
         * @return the error code
120
         */
121
        public static ErrorCode getErrorCode(int errorNumber) {
122
            for (ErrorCode code : ErrorCode.values()) {
1✔
123
                if (code.errorCode == errorNumber) {
1✔
124
                    return code;
1✔
125
                }
126
            }
127

128
            return null;
×
129
        }
130

131
        /**
132
         * Gets the exception.
133
         *
134
         * @return the exception
135
         * @throws InstantiationException the instantiation exception
136
         * @throws IllegalAccessException the illegal access exception
137
         */
138
        public SmartsheetRestException getException() throws InstantiationException, IllegalAccessException {
139
            return exceptionClass.newInstance();
×
140
        }
141

142
        /**
143
         * Gets the exception.
144
         *
145
         * @param error the error
146
         * @return the exception
147
         * @throws SmartsheetException the smartsheet exception
148
         */
149
        public SmartsheetRestException getException(com.smartsheet.api.models.Error error) throws SmartsheetException {
150

151
            try {
152
                return exceptionClass.getConstructor(com.smartsheet.api.models.Error.class).newInstance(error);
1✔
153
            } catch (IllegalArgumentException e) {
×
154
                throw new SmartsheetException(e);
×
155
            } catch (SecurityException e) {
×
156
                throw new SmartsheetException(e);
×
157
            } catch (InstantiationException e) {
×
158
                throw new SmartsheetException(e);
×
159
            } catch (IllegalAccessException e) {
×
160
                throw new SmartsheetException(e);
×
161
            } catch (InvocationTargetException e) {
×
162
                throw new SmartsheetException(e);
×
163
            } catch (NoSuchMethodException e) {
×
164
                throw new SmartsheetException(e);
×
165
            }
166
        }
167
    }
168

169
    /**
170
     * Represents the SmartsheetImpl.
171
     * <p>
172
     * It will be initialized in constructor and will not change afterwards.
173
     */
174
    protected final SmartsheetImpl smartsheet;
175

176
    /**
177
     * Constructor.
178
     *
179
     * @param smartsheet the smartsheet
180
     */
181
    protected AbstractResources(SmartsheetImpl smartsheet) {
1✔
182
        Util.throwIfNull(smartsheet);
1✔
183

184
        this.smartsheet = smartsheet;
1✔
185
    }
1✔
186

187
    /**
188
     * Get a resource from Smartsheet REST API.
189
     * <p>
190
     * Parameters: - path : the relative path of the resource - objectClass : the resource object class
191
     * <p>
192
     * Returns: the resource (note that if there is no such resource, this method will throw ResourceNotFoundException
193
     * rather than returning null).
194
     * <p>
195
     * Exceptions: -
196
     * InvalidRequestException : if there is any problem with the REST API request
197
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
198
     * ResourceNotFoundException : if the resource can not be found
199
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
200
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
201
     * SmartsheetException : if there is any other error occurred during the operation
202
     *
203
     * @param <T>         the generic type
204
     * @param path        the relative path of the resource.
205
     * @param objectClass the object class
206
     * @return the resource
207
     * @throws SmartsheetException the smartsheet exception
208
     */
209
    protected <T> T getResource(String path, Class<T> objectClass) throws SmartsheetException {
210
        Util.throwIfNull(path, objectClass);
1✔
211

212
        if (path.isEmpty()) {
1✔
213
            com.smartsheet.api.models.Error error = new com.smartsheet.api.models.Error();
×
214
            error.setMessage("An empty path was provided.");
×
215
            throw new ResourceNotFoundException(error);
×
216
        }
217

218
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.GET);
1✔
219

220
        T obj = null;
1✔
221
        String content = null;
1✔
222
        try {
223
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
224
            InputStream inputStream = response.getEntity().getContent();
1✔
225
            switch (response.getStatusCode()) {
1✔
226
                case 200:
227
                    try {
228
                        if (log.isInfoEnabled()) {
1✔
229
                            ByteArrayOutputStream contentCopyStream = new ByteArrayOutputStream();
1✔
230
                            inputStream = StreamUtil.cloneContent(inputStream, response.getEntity().getContentLength(), contentCopyStream);
1✔
231
                            content = StreamUtil.toUtf8StringOrHex(contentCopyStream, getResponseLogLength());
1✔
232
                        }
233
                        obj = this.smartsheet.getJsonSerializer().deserialize(objectClass, inputStream);
1✔
234
                    } catch (JsonParseException e) {
×
235
                        log.info("failure parsing '{}'", content, e);
×
236
                        throw new SmartsheetException(e);
×
237
                    } catch (JsonMappingException e) {
×
238
                        log.info("failure mapping '{}'", content, e);
×
239
                        throw new SmartsheetException(e);
×
240
                    } catch (IOException e) {
×
241
                        log.info("failure loading '{}'", content, e);
×
242
                        throw new SmartsheetException(e);
×
243
                    }
1✔
244
                    break;
245
                default:
246
                    handleError(response);
×
247
            }
248
        } catch (JSONSerializerException jsx) {
×
249
            log.info("failed to parse '{}'", content, jsx);
×
250
            throw jsx;
×
251
        } finally {
252
            smartsheet.getHttpClient().releaseConnection();
1✔
253
        }
254
        return obj;
1✔
255
    }
256

257
    /**
258
     * Create a resource using Smartsheet REST API.
259
     * <p>
260
     * Exceptions:
261
     * IllegalArgumentException : if any argument is null, or path is empty string
262
     * InvalidRequestException : if there is any problem with the REST API request
263
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
264
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
265
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
266
     * SmartsheetException : if there is any other error occurred during the operation
267
     *
268
     * @param <T>         the generic type of object to return/deserialize
269
     * @param <S>         the generic type of object to serialize
270
     * @param path        the relative path of the resource collections
271
     * @param objectClass the resource object class
272
     * @param object      the object to create
273
     * @return the created resource
274
     * @throws SmartsheetException the smartsheet exception
275
     */
276
    protected <T, S> T createResource(String path, Class<T> objectClass, S object) throws SmartsheetException {
277
        Util.throwIfNull(path, object, objectClass);
1✔
278
        Util.throwIfEmpty(path);
1✔
279

280
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST);
1✔
281

282
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
283
        this.smartsheet.getJsonSerializer().serialize(object, objectBytesStream);
1✔
284
        HttpEntity entity = new HttpEntity();
1✔
285
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
286
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
287
        entity.setContentLength(objectBytesStream.size());
1✔
288
        request.setEntity(entity);
1✔
289

290
        T obj = null;
1✔
291
        try {
292
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
293
            switch (response.getStatusCode()) {
1✔
294
                case 200: {
295
                    InputStream inputStream = response.getEntity().getContent();
1✔
296
                    String content = null;
1✔
297
                    try {
298
                        if (log.isInfoEnabled()) {
1✔
299
                            ByteArrayOutputStream contentCopyStream = new ByteArrayOutputStream();
1✔
300
                            inputStream = StreamUtil.cloneContent(inputStream, response.getEntity().getContentLength(), contentCopyStream);
1✔
301
                            content = StreamUtil.toUtf8StringOrHex(contentCopyStream, getResponseLogLength());
1✔
302
                        }
303
                        obj = this.smartsheet.getJsonSerializer().deserializeResult(objectClass, inputStream).getResult();
1✔
304
                    } catch (JSONSerializerException e) {
×
305
                        log.info("failure parsing '{}'", content, e);
×
306
                        throw new SmartsheetException(e);
×
307
                    } catch (IOException e) {
×
308
                        log.info("failure cloning content from inputStream '{}'", inputStream, e);
×
309
                        throw new SmartsheetException(e);
×
310
                    }
1✔
311
                    break;
312
                }
313
                default:
314
                    handleError(response);
×
315
            }
316
        } finally {
317
            smartsheet.getHttpClient().releaseConnection();
1✔
318
        }
319

320
        return obj;
1✔
321
    }
322

323
    /**
324
     * Create a resource using Smartsheet REST API.
325
     * <p>
326
     * Exceptions:
327
     * IllegalArgumentException : if any argument is null, or path is empty string
328
     * InvalidRequestException : if there is any problem with the REST API request
329
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
330
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
331
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
332
     * SmartsheetException : if there is any other error occurred during the operation
333
     *
334
     * @param <T>         the generic type
335
     * @param path        the relative path of the resource collections
336
     * @param objectClass the resource object class
337
     * @param object      the object to create
338
     * @return the created resource
339
     * @throws SmartsheetException the smartsheet exception
340
     */
341
    protected <T> T createResourceWithAttachment(
342
            String path,
343
            Class<T> objectClass,
344
            T object,
345
            String partName,
346
            InputStream inputStream,
347
            String contentType,
348
            String attachmentName
349
    ) throws SmartsheetException {
350
        Util.throwIfNull(path, object);
1✔
351
        Util.throwIfEmpty(path);
1✔
352

353
        HttpRequest request;
354
        final String boundary = "----" + System.currentTimeMillis();
1✔
355
        CloseableHttpClient httpClient = HttpClients.createDefault();
1✔
356
        HttpPost uploadFile = createHttpPost(this.getSmartsheet().getBaseURI().resolve(path));
1✔
357

358
        try {
359
            uploadFile.setHeader(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
1✔
360
        } catch (Exception e) {
×
361
            throw new RuntimeException(e);
×
362
        }
1✔
363

364
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
1✔
365
        builder.setBoundary(boundary);
1✔
366
        builder.addTextBody(partName, this.getSmartsheet().getJsonSerializer().serialize(object), ContentType.APPLICATION_JSON);
1✔
367
        builder.addBinaryBody("file", inputStream, ContentType.create(contentType), attachmentName);
1✔
368
        org.apache.http.HttpEntity multipart = builder.build();
1✔
369

370
        uploadFile.setEntity(multipart);
1✔
371

372
        T obj = null;
1✔
373
        //implement switch case
374
        try {
375
            CloseableHttpResponse response = httpClient.execute(uploadFile);
1✔
376
            org.apache.http.HttpEntity responseEntity = response.getEntity();
1✔
377
            obj = this.getSmartsheet().getJsonSerializer().deserializeResult(objectClass,
1✔
378
                    responseEntity.getContent()).getResult();
1✔
379
        } catch (Exception e) {
×
380
            throw new RuntimeException(e);
×
381
        }
1✔
382
        return obj;
1✔
383
    }
384

385
    /**
386
     * Update a resource using Smartsheet REST API.
387
     * <p>
388
     * Exceptions:
389
     * IllegalArgumentException : if any argument is null, or path is empty string
390
     * InvalidRequestException : if there is any problem with the REST API request
391
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
392
     * ResourceNotFoundException : if the resource can not be found
393
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
394
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
395
     * SmartsheetException : if there is any other error occurred during the operation
396
     *
397
     * @param <T>         the generic type
398
     * @param path        the relative path of the resource
399
     * @param objectClass the resource object class
400
     * @param object      the object to create
401
     * @return the updated resource
402
     * @throws SmartsheetException the smartsheet exception
403
     */
404
    protected <T> T updateResource(String path, Class<T> objectClass, T object) throws SmartsheetException {
405
        Util.throwIfNull(path, object);
1✔
406
        Util.throwIfEmpty(path);
1✔
407

408
        HttpRequest request;
409
        request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.PUT);
1✔
410

411
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
412
        this.smartsheet.getJsonSerializer().serialize(object, objectBytesStream);
1✔
413
        HttpEntity entity = new HttpEntity();
1✔
414
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
415
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
416
        entity.setContentLength(objectBytesStream.size());
1✔
417
        request.setEntity(entity);
1✔
418

419
        T obj = null;
1✔
420
        try {
421
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
422
            switch (response.getStatusCode()) {
1✔
423
                case 200:
424
                    obj = this.smartsheet.getJsonSerializer().deserializeResult(objectClass,
1✔
425
                            response.getEntity().getContent()).getResult();
1✔
426
                    break;
1✔
427
                default:
428
                    handleError(response);
×
429
            }
430
        } finally {
431
            smartsheet.getHttpClient().releaseConnection();
1✔
432
        }
433

434
        return obj;
1✔
435
    }
436

437
    /**
438
     * List resources using Smartsheet REST API.
439
     * <p>
440
     * Exceptions:
441
     * IllegalArgumentException : if any argument is null, or path is empty string
442
     * InvalidRequestException : if there is any problem with the REST API request
443
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
444
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
445
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
446
     * SmartsheetException : if there is any other error occurred during the operation
447
     *
448
     * @param <T>         the generic type
449
     * @param path        the relative path of the resource collections
450
     * @param objectClass the resource object class
451
     * @return the resources
452
     * @throws SmartsheetException if an error occurred during the operation
453
     */
454
    protected <T> List<T> listResources(String path, Class<T> objectClass) throws SmartsheetException {
455
        Util.throwIfNull(path, objectClass);
×
456
        Util.throwIfEmpty(path);
×
457

458
        HttpRequest request;
459
        request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.GET);
×
460

461
        List<T> obj = null;
×
462
        try {
463
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
×
464
            switch (response.getStatusCode()) {
×
465
                case 200:
466
                    obj = this.smartsheet.getJsonSerializer().deserializeList(objectClass,
×
467
                            response.getEntity().getContent());
×
468
                    break;
×
469
                default:
470
                    handleError(response);
×
471
            }
472
        } finally {
473
            smartsheet.getHttpClient().releaseConnection();
×
474
        }
475

476
        return obj;
×
477
    }
478

479
    /**
480
     * List resources Wrapper (supports paging info) using Smartsheet REST API.
481
     *
482
     * @throws IllegalArgumentException    : if any argument is null, or path is empty string
483
     * @throws InvalidRequestException     : if there is any problem with the REST API request
484
     * @throws AuthorizationException      : if there is any problem with the REST API authorization(access token)
485
     * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
486
     * @throws SmartsheetRestException     : if there is any other REST API related error occurred during the operation
487
     * @throws SmartsheetException         : if there is any other error occurred during the operation
488
     */
489
    protected <T> PagedResult<T> listResourcesWithWrapper(String path, Class<T> objectClass) throws SmartsheetException {
490
        Util.throwIfNull(path, objectClass);
1✔
491
        Util.throwIfEmpty(path);
1✔
492

493
        HttpRequest request;
494
        request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.GET);
1✔
495

496
        PagedResult<T> obj = null;
1✔
497
        try {
498
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
499
            switch (response.getStatusCode()) {
1✔
500
                case 200:
501
                    obj = this.smartsheet.getJsonSerializer().deserializeDataWrapper(objectClass,
1✔
502
                            response.getEntity().getContent());
1✔
503
                    break;
1✔
504
                default:
505
                    handleError(response);
×
506
            }
507
        } finally {
508
            smartsheet.getHttpClient().releaseConnection();
1✔
509
        }
510

511
        return obj;
1✔
512
    }
513

514
    /**
515
     * Delete a resource from Smartsheet REST API.
516
     * <p>
517
     * Exceptions:
518
     * IllegalArgumentException : if any argument is null, or path is empty string
519
     * InvalidRequestException : if there is any problem with the REST API request
520
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
521
     * ResourceNotFoundException : if the resource can not be found
522
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
523
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
524
     * SmartsheetException : if there is any other error occurred during the operation
525
     *
526
     * @param <T>         the generic type
527
     * @param path        the relative path of the resource
528
     * @param objectClass the resource object class
529
     * @throws SmartsheetException the smartsheet exception
530
     */
531
    protected <T> void deleteResource(String path, Class<T> objectClass) throws SmartsheetException {
532
        Util.throwIfNull(path, objectClass);
1✔
533
        Util.throwIfEmpty(path);
1✔
534

535
        HttpRequest request;
536
        request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.DELETE);
1✔
537

538
        try {
539
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
540
            switch (response.getStatusCode()) {
1✔
541
                case 200:
542
                    this.smartsheet.getJsonSerializer().deserializeResult(objectClass,
1✔
543
                            response.getEntity().getContent());
1✔
544
                    break;
1✔
545
                default:
546
                    handleError(response);
×
547
            }
548
        } finally {
549
            smartsheet.getHttpClient().releaseConnection();
1✔
550
        }
551
    }
1✔
552

553
    /**
554
     * Delete resources and return a list from Smartsheet REST API.
555
     * <p>
556
     * Exceptions:
557
     * IllegalArgumentException : if any argument is null, or path is empty string
558
     * InvalidRequestException : if there is any problem with the REST API request
559
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
560
     * ResourceNotFoundException : if the resource can not be found
561
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
562
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
563
     * SmartsheetException : if there is any other error occurred during the operation
564
     *
565
     * @param <T>         the generic type
566
     * @param path        the relative path of the resource
567
     * @param objectClass the resource object class
568
     * @return List of ids deleted
569
     * @throws SmartsheetException the smartsheet exception
570
     */
571
    protected <T> List<T> deleteListResources(String path, Class<T> objectClass) throws SmartsheetException {
572
        Util.throwIfNull(path, objectClass);
1✔
573
        Util.throwIfEmpty(path);
1✔
574

575
        Result<List<T>> obj = null;
1✔
576
        HttpRequest request;
577
        request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.DELETE);
1✔
578
        try {
579
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
580
            switch (response.getStatusCode()) {
1✔
581
                case 200:
582
                    obj = this.smartsheet.getJsonSerializer().deserializeListResult(objectClass,
1✔
583
                            response.getEntity().getContent());
1✔
584
                    break;
1✔
585
                default:
586
                    handleError(response);
×
587
            }
588
        } finally {
589
            smartsheet.getHttpClient().releaseConnection();
1✔
590
        }
591
        return obj.getResult();
1✔
592
    }
593

594
    /**
595
     * Post an object to Smartsheet REST API and receive a list of objects from response.
596
     * <p>
597
     * Parameters: - path : the relative path of the resource collections - objectToPost : the object to post -
598
     * objectClassToReceive : the resource object class to receive
599
     * <p>
600
     * Returns: the object list
601
     * <p>
602
     * Exceptions:
603
     * IllegalArgumentException : if any argument is null, or path is empty string
604
     * InvalidRequestException : if there is any problem with the REST API request
605
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
606
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
607
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
608
     * SmartsheetException : if there is any other error occurred during the operation
609
     *
610
     * @param <T>                  the generic type
611
     * @param <S>                  the generic type
612
     * @param path                 the path
613
     * @param objectToPost         the object to post
614
     * @param objectClassToReceive the object class to receive
615
     * @return the list
616
     * @throws SmartsheetException the smartsheet exception
617
     */
618
    protected <T, S> List<S> postAndReceiveList(String path, T objectToPost, Class<S> objectClassToReceive) throws SmartsheetException {
619
        Util.throwIfNull(path, objectToPost, objectClassToReceive);
1✔
620
        Util.throwIfEmpty(path);
1✔
621

622
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST);
1✔
623

624
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
625
        this.smartsheet.getJsonSerializer().serialize(objectToPost, objectBytesStream);
1✔
626
        HttpEntity entity = new HttpEntity();
1✔
627
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
628
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
629
        entity.setContentLength(objectBytesStream.size());
1✔
630
        request.setEntity(entity);
1✔
631

632
        List<S> obj = null;
1✔
633
        try {
634
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
635
            switch (response.getStatusCode()) {
1✔
636
                case 200:
637
                    obj = this.smartsheet.getJsonSerializer().deserializeListResult(objectClassToReceive,
1✔
638
                            response.getEntity().getContent()).getResult();
1✔
639
                    break;
1✔
640
                default:
641
                    handleError(response);
×
642
            }
643
        } finally {
644
            smartsheet.getHttpClient().releaseConnection();
1✔
645
        }
646

647
        return obj;
1✔
648
    }
649

650
    /**
651
     * Partially update a resource using Smartsheet REST API with PATCH method.
652
     * <p>
653
     * Exceptions:
654
     * IllegalArgumentException : if any argument is null, or path is empty string
655
     * InvalidRequestException : if there is any problem with the REST API request
656
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
657
     * ResourceNotFoundException : if the resource can not be found
658
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
659
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
660
     * SmartsheetException : if there is any other error occurred during the operation
661
     *
662
     * @param <T>         the generic type
663
     * @param path        the relative path of the resource
664
     * @param objectClass the resource object class
665
     * @param object      the object to patch
666
     * @return the updated resource
667
     * @throws SmartsheetException the smartsheet exception
668
     */
669
    protected <T> T patchResource(String path, Class<T> objectClass, Object object) throws SmartsheetException {
670
        Util.throwIfNull(path, object);
1✔
671
        Util.throwIfEmpty(path);
1✔
672

673
        HttpRequest request;
674
        request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.PATCH);
1✔
675

676
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
677
        this.smartsheet.getJsonSerializer().serialize(object, objectBytesStream);
1✔
678
        HttpEntity entity = new HttpEntity();
1✔
679
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
680
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
681
        entity.setContentLength(objectBytesStream.size());
1✔
682
        request.setEntity(entity);
1✔
683

684
        T obj = null;
1✔
685
        try {
686
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
687
            if (response.getStatusCode() == 200) {
1✔
688
                obj = this.smartsheet.getJsonSerializer().deserialize(objectClass,
1✔
689
                        response.getEntity().getContent());
1✔
690
            } else {
NEW
691
                handleError(response);
×
692
            }
NEW
693
        } catch (IOException e) {
×
NEW
694
            throw new RuntimeException(e);
×
695
        } finally {
696
            smartsheet.getHttpClient().releaseConnection();
1✔
697
        }
698

699
        return obj;
1✔
700
    }
701

702
    /**
703
     * Post an object to Smartsheet REST API and receive a CopyOrMoveRowResult object from response.
704
     * <p>
705
     * Parameters: - path : the relative path of the resource collections - objectToPost : the object to post -
706
     * <p>
707
     * Returns: the object
708
     * <p>
709
     * Exceptions:
710
     * IllegalArgumentException : if any argument is null, or path is empty string
711
     * InvalidRequestException : if there is any problem with the REST API request
712
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
713
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
714
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
715
     * SmartsheetException : if there is any other error occurred during the operation
716
     *
717
     * @param path         the path
718
     * @param objectToPost the object to post
719
     * @return the result object
720
     * @throws SmartsheetException the smartsheet exception
721
     */
722
    protected CopyOrMoveRowResult postAndReceiveRowObject(String path, CopyOrMoveRowDirective objectToPost) throws SmartsheetException {
723
        Util.throwIfNull(path, objectToPost);
1✔
724
        Util.throwIfEmpty(path);
1✔
725

726
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST);
1✔
727

728
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
729
        this.smartsheet.getJsonSerializer().serialize(objectToPost, objectBytesStream);
1✔
730
        HttpEntity entity = new HttpEntity();
1✔
731
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
732
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
733
        entity.setContentLength(objectBytesStream.size());
1✔
734
        request.setEntity(entity);
1✔
735

736
        CopyOrMoveRowResult obj = null;
1✔
737
        try {
738
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
739
            switch (response.getStatusCode()) {
1✔
740
                case 200:
741
                    obj = this.smartsheet.getJsonSerializer().deserializeCopyOrMoveRow(
1✔
742
                            response.getEntity().getContent());
1✔
743
                    break;
1✔
744
                default:
745
                    handleError(response);
×
746
            }
747
        } finally {
748
            smartsheet.getHttpClient().releaseConnection();
1✔
749
        }
750

751
        return obj;
1✔
752
    }
753

754
    /**
755
     * Put an object to Smartsheet REST API and receive a list of objects from response.
756
     *
757
     * @param <T>                  the generic type
758
     * @param <S>                  the generic type
759
     * @param path                 the relative path of the resource collections
760
     * @param objectToPut          the object to put
761
     * @param objectClassToReceive the resource object class to receive
762
     * @return the object list
763
     * @throws IllegalArgumentException    : if any argument is null, or path is empty string
764
     * @throws InvalidRequestException     : if there is any problem with the REST API request
765
     * @throws AuthorizationException      : if there is any problem with the REST API authorization(access token)
766
     * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
767
     * @throws SmartsheetRestException     : if there is any other REST API related error occurred during the operation
768
     * @throws SmartsheetException         : if there is any other error occurred during the operation
769
     */
770
    protected <T, S> List<S> putAndReceiveList(String path, T objectToPut, Class<S> objectClassToReceive)
771
            throws SmartsheetException {
772
        Util.throwIfNull(path, objectToPut, objectClassToReceive);
1✔
773
        Util.throwIfEmpty(path);
1✔
774

775
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.PUT);
1✔
776

777
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
778
        this.smartsheet.getJsonSerializer().serialize(objectToPut, objectBytesStream);
1✔
779
        HttpEntity entity = new HttpEntity();
1✔
780
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
781
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
782
        entity.setContentLength(objectBytesStream.size());
1✔
783
        request.setEntity(entity);
1✔
784

785
        List<S> obj = null;
1✔
786
        try {
787
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
788
            switch (response.getStatusCode()) {
1✔
789
                case 200:
790
                    obj = this.smartsheet.getJsonSerializer().deserializeListResult(
1✔
791
                            objectClassToReceive, response.getEntity().getContent()).getResult();
1✔
792
                    break;
1✔
793
                default:
794
                    handleError(response);
×
795
            }
796
        } finally {
797
            smartsheet.getHttpClient().releaseConnection();
1✔
798
        }
799

800
        return obj;
1✔
801
    }
802

803
    /**
804
     * Create an HttpRequest.
805
     *
806
     * @param uri    the URI
807
     * @param method the HttpMethod
808
     * @return the http request
809
     */
810
    protected HttpRequest createHttpRequest(URI uri, HttpMethod method) {
811
        HttpRequest request = new HttpRequest();
1✔
812
        request.setUri(uri);
1✔
813
        request.setMethod(method);
1✔
814

815
        // Set authorization header
816
        request.setHeaders(createHeaders());
1✔
817

818
        return request;
1✔
819
    }
820

821
    protected HttpPost createHttpPost(URI uri) {
822
        HttpPost httpPost = new HttpPost(uri);
1✔
823
        Map<String, String> headers = createHeaders();
1✔
824
        for (Map.Entry<String, String> entry : headers.entrySet()) {
1✔
825
            httpPost.addHeader(entry.getKey(), entry.getValue());
1✔
826
        }
1✔
827
        return httpPost;
1✔
828
    }
829

830
    /**
831
     * Attach a file
832
     */
833
    public Attachment attachFile(String url, InputStream inputStream, String contentType, long contentLength, String attachmentName)
834
            throws SmartsheetException {
835
        Util.throwIfNull(inputStream, contentType);
1✔
836
        HttpRequest request = createHttpRequest(this.getSmartsheet().getBaseURI().resolve(url), HttpMethod.POST);
1✔
837
        request.getHeaders().put(
1✔
838
                "Content-Disposition",
839
                "attachment; filename=\"" + URLEncoder.encode(attachmentName, StandardCharsets.UTF_8) + "\""
1✔
840
        );
841
        HttpEntity entity = new HttpEntity();
1✔
842
        entity.setContentType(contentType);
1✔
843
        entity.setContent(new LengthEnforcingInputStream(inputStream, contentLength));
1✔
844
        entity.setContentLength(contentLength);
1✔
845
        request.setEntity(entity);
1✔
846

847
        Attachment attachment = null;
1✔
848
        try {
849
            HttpResponse response = this.getSmartsheet().getHttpClient().request(request);
1✔
850
            switch (response.getStatusCode()) {
1✔
851
                case 200:
852
                    attachment = this.getSmartsheet().getJsonSerializer().deserializeResult(Attachment.class,
1✔
853
                            response.getEntity().getContent()).getResult();
1✔
854
                    break;
1✔
855
                default:
856
                    handleError(response);
×
857
            }
858
        } finally {
859
            this.getSmartsheet().getHttpClient().releaseConnection();
1✔
860
        }
861

862
        return attachment;
1✔
863
    }
864

865
    /**
866
     * Create a multipart upload request.
867
     *
868
     * @param url         the url
869
     * @param t           the object to create
870
     * @param partName    the name of the part
871
     * @param inputstream the file inputstream
872
     * @param contentType the type of the file to be attached
873
     * @return the http request
874
     * @throws SmartsheetException may be thrown in the method
875
     */
876
    public <T> Attachment attachFile(String url, T t, String partName, InputStream inputstream, String contentType, String attachmentName)
877
            throws SmartsheetException {
878
        Util.throwIfNull(inputstream, contentType);
×
879
        Attachment attachment = null;
×
880
        final String boundary = "----" + System.currentTimeMillis();
×
881

882
        CloseableHttpClient httpClient = HttpClients.createDefault();
×
883
        HttpPost uploadFile = createHttpPost(this.getSmartsheet().getBaseURI().resolve(url));
×
884

885
        try {
886
            uploadFile.setHeader(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
×
887
        } catch (Exception e) {
×
888
            throw new RuntimeException(e);
×
889
        }
×
890

891
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
×
892
        builder.setBoundary(boundary);
×
893
        builder.addTextBody(partName, this.getSmartsheet().getJsonSerializer().serialize(t), ContentType.APPLICATION_JSON);
×
894
        builder.addBinaryBody("file", inputstream, ContentType.create(contentType), attachmentName);
×
895
        org.apache.http.HttpEntity multipart = builder.build();
×
896

897
        uploadFile.setEntity(multipart);
×
898

899
        try {
900
            CloseableHttpResponse response = httpClient.execute(uploadFile);
×
901
            org.apache.http.HttpEntity responseEntity = response.getEntity();
×
902
            attachment = this.getSmartsheet().getJsonSerializer().deserializeResult(Attachment.class,
×
903
                    responseEntity.getContent()).getResult();
×
904
        } catch (Exception e) {
×
905
            throw new RuntimeException(e);
×
906
        }
×
907
        return attachment;
×
908
    }
909

910
    /**
911
     * Handles an error HttpResponse (non-200) returned by Smartsheet REST API.
912
     *
913
     * @param response the HttpResponse
914
     * @throws SmartsheetException     the smartsheet exception
915
     * @throws SmartsheetRestException : the exception corresponding to the error
916
     */
917
    protected void handleError(HttpResponse response) throws SmartsheetException {
918

919
        com.smartsheet.api.models.Error error;
920
        try {
921
            error = this.smartsheet.getJsonSerializer().deserialize(
1✔
922
                    com.smartsheet.api.models.Error.class, response.getEntity().getContent());
1✔
923
        } catch (IOException e) {
×
924
            throw new SmartsheetException(e);
×
925
        }
1✔
926

927
        ErrorCode code = ErrorCode.getErrorCode(response.getStatusCode());
1✔
928

929
        if (code == null) {
1✔
930
            throw new SmartsheetRestException(error);
×
931
        }
932

933
        try {
934
            throw code.getException(error);
1✔
935
        } catch (IllegalArgumentException e) {
×
936
            throw new SmartsheetException(e);
×
937
        } catch (SecurityException e) {
×
938
            throw new SmartsheetException(e);
×
939
        }
940
    }
941

942
    /**
943
     * Gets the smartsheet.
944
     *
945
     * @return the smartsheet
946
     */
947
    public SmartsheetImpl getSmartsheet() {
948
        return smartsheet;
1✔
949
    }
950

951
    /**
952
     * Get a sheet as a file.
953
     *
954
     * @param path         the path
955
     * @param fileType     the output file type
956
     * @param outputStream the OutputStream to which the file will be written
957
     * @throws InvalidRequestException     : if there is any problem with the REST API request
958
     * @throws AuthorizationException      : if there is any problem with the REST API authorization(access token)
959
     * @throws ResourceNotFoundException   : if the resource can not be found
960
     * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
961
     * @throws SmartsheetRestException     : if there is any other REST API related error occurred during the operation
962
     * @throws SmartsheetException         : if there is any other error occurred during the operation
963
     */
964
    public void getResourceAsFile(String path, String fileType, OutputStream outputStream)
965
            throws SmartsheetException {
966
        Util.throwIfNull(outputStream, fileType);
1✔
967

968
        HttpRequest request;
969
        request = createHttpRequest(this.getSmartsheet().getBaseURI().resolve(path), HttpMethod.GET);
1✔
970
        request.getHeaders().put("Accept", fileType);
1✔
971

972
        try {
973
            HttpResponse response = getSmartsheet().getHttpClient().request(request);
1✔
974

975
            switch (response.getStatusCode()) {
1✔
976
                case 200:
977
                    try {
978
                        copyStream(response.getEntity().getContent(), outputStream);
1✔
979
                    } catch (IOException e) {
×
980
                        throw new SmartsheetException(e);
×
981
                    }
1✔
982
                    break;
983
                default:
984
                    handleError(response);
×
985
            }
986
        } finally {
987
            getSmartsheet().getHttpClient().releaseConnection();
1✔
988
        }
989
    }
1✔
990

991
    /*
992
     * Copy an input stream to an output stream.
993
     *
994
     * @param input The input stream to copy.
995
     *
996
     * @param output the output stream to write to.
997
     *
998
     * @throws IOException if there is trouble reading or writing to the streams.
999
     */
1000

1001
    /**
1002
     * Copy stream.
1003
     *
1004
     * @param input  the input
1005
     * @param output the output
1006
     * @throws IOException Signals that an I/O exception has occurred.
1007
     * @deprecated replace with StreamUtil.copyContentIntoOutputStream()
1008
     */
1009
    @Deprecated(since = "2.0.0", forRemoval = true)
1010
    private static void copyStream(InputStream input, OutputStream output) throws IOException {
1011
        byte[] buffer = new byte[BUFFER_SIZE];
1✔
1012
        int len;
1013
        while ((len = input.read(buffer)) != -1) {
1✔
1014
            output.write(buffer, 0, len);
1✔
1015
        }
1016
    }
1✔
1017

1018
    /**
1019
     * @return a map of headers to be used when making requests.
1020
     */
1021
    Map<String, String> createHeaders() {
1022
        Map<String, String> headers = new HashMap<>();
1✔
1023
        headers.put("Authorization", "Bearer " + smartsheet.getAccessToken());
1✔
1024
        headers.put(HEADER_CONTENT_TYPE, JSON_CONTENT_TYPE);
1✔
1025

1026
        // Set assumed user
1027
        if (smartsheet.getAssumedUser() != null) {
1✔
1028
            headers.put("Assume-User", URLEncoder.encode(smartsheet.getAssumedUser(), StandardCharsets.UTF_8));
×
1029
        }
1030
        if (smartsheet.getChangeAgent() != null) {
1✔
1031
            headers.put("Smartsheet-Change-Agent", URLEncoder.encode(smartsheet.getChangeAgent(), StandardCharsets.UTF_8));
1✔
1032
        }
1033
        if (smartsheet.getUserAgent() != null) {
1✔
1034
            headers.put("User-Agent", smartsheet.getUserAgent());
1✔
1035
        }
1036
        return headers;
1✔
1037
    }
1038

1039
    int getResponseLogLength() {
1040
        // not cached to allow for it to be changed dynamically by client code
1041
        return Integer.getInteger(PROPERTY_RESPONSE_LOG_CHARS, 1024);
1✔
1042
    }
1043
}
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