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

smartsheet / smartsheet-java-sdk / #55

02 Oct 2024 07:40PM UTC coverage: 60.548% (+0.7%) from 59.836%
#55

push

github

web-flow
Release prep for 3.2.1 (#103)

4156 of 6864 relevant lines covered (60.55%)

0.61 hits per line

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

70.97
/src/main/java/com/smartsheet/api/internal/AbstractResources.java
1
/*
2
 * Copyright (C) 2024 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
     * Post an object to Smartsheet REST API and receive a CopyOrMoveRowResult object from response.
652
     * <p>
653
     * Parameters: - path : the relative path of the resource collections - objectToPost : the object to post -
654
     * <p>
655
     * Returns: the object
656
     * <p>
657
     * Exceptions:
658
     * IllegalArgumentException : if any argument is null, or path is empty string
659
     * InvalidRequestException : if there is any problem with the REST API request
660
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
661
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
662
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
663
     * SmartsheetException : if there is any other error occurred during the operation
664
     *
665
     * @param path         the path
666
     * @param objectToPost the object to post
667
     * @return the result object
668
     * @throws SmartsheetException the smartsheet exception
669
     */
670
    protected CopyOrMoveRowResult postAndReceiveRowObject(String path, CopyOrMoveRowDirective objectToPost) throws SmartsheetException {
671
        Util.throwIfNull(path, objectToPost);
1✔
672
        Util.throwIfEmpty(path);
1✔
673

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

676
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
677
        this.smartsheet.getJsonSerializer().serialize(objectToPost, 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
        CopyOrMoveRowResult obj = null;
1✔
685
        try {
686
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
687
            switch (response.getStatusCode()) {
1✔
688
                case 200:
689
                    obj = this.smartsheet.getJsonSerializer().deserializeCopyOrMoveRow(
1✔
690
                            response.getEntity().getContent());
1✔
691
                    break;
1✔
692
                default:
693
                    handleError(response);
×
694
            }
695
        } finally {
696
            smartsheet.getHttpClient().releaseConnection();
1✔
697
        }
698

699
        return obj;
1✔
700
    }
701

702
    /**
703
     * Put an object to Smartsheet REST API and receive a list of objects from response.
704
     *
705
     * @param <T>                  the generic type
706
     * @param <S>                  the generic type
707
     * @param path                 the relative path of the resource collections
708
     * @param objectToPut          the object to put
709
     * @param objectClassToReceive the resource object class to receive
710
     * @return the object list
711
     * @throws IllegalArgumentException    : if any argument is null, or path is empty string
712
     * @throws InvalidRequestException     : if there is any problem with the REST API request
713
     * @throws AuthorizationException      : if there is any problem with the REST API authorization(access token)
714
     * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
715
     * @throws SmartsheetRestException     : if there is any other REST API related error occurred during the operation
716
     * @throws SmartsheetException         : if there is any other error occurred during the operation
717
     */
718
    protected <T, S> List<S> putAndReceiveList(String path, T objectToPut, Class<S> objectClassToReceive)
719
            throws SmartsheetException {
720
        Util.throwIfNull(path, objectToPut, objectClassToReceive);
1✔
721
        Util.throwIfEmpty(path);
1✔
722

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

725
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
1✔
726
        this.smartsheet.getJsonSerializer().serialize(objectToPut, objectBytesStream);
1✔
727
        HttpEntity entity = new HttpEntity();
1✔
728
        entity.setContentType(JSON_CONTENT_TYPE);
1✔
729
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
1✔
730
        entity.setContentLength(objectBytesStream.size());
1✔
731
        request.setEntity(entity);
1✔
732

733
        List<S> obj = null;
1✔
734
        try {
735
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
1✔
736
            switch (response.getStatusCode()) {
1✔
737
                case 200:
738
                    obj = this.smartsheet.getJsonSerializer().deserializeListResult(
1✔
739
                            objectClassToReceive, response.getEntity().getContent()).getResult();
1✔
740
                    break;
1✔
741
                default:
742
                    handleError(response);
×
743
            }
744
        } finally {
745
            smartsheet.getHttpClient().releaseConnection();
1✔
746
        }
747

748
        return obj;
1✔
749
    }
750

751
    /**
752
     * Create an HttpRequest.
753
     *
754
     * @param uri    the URI
755
     * @param method the HttpMethod
756
     * @return the http request
757
     */
758
    protected HttpRequest createHttpRequest(URI uri, HttpMethod method) {
759
        HttpRequest request = new HttpRequest();
1✔
760
        request.setUri(uri);
1✔
761
        request.setMethod(method);
1✔
762

763
        // Set authorization header
764
        request.setHeaders(createHeaders());
1✔
765

766
        return request;
1✔
767
    }
768

769
    protected HttpPost createHttpPost(URI uri) {
770
        HttpPost httpPost = new HttpPost(uri);
1✔
771
        Map<String, String> headers = createHeaders();
1✔
772
        for (Map.Entry<String, String> entry : headers.entrySet()) {
1✔
773
            httpPost.addHeader(entry.getKey(), entry.getValue());
1✔
774
        }
1✔
775
        return httpPost;
1✔
776
    }
777

778
    /**
779
     * Attach a file
780
     */
781
    public Attachment attachFile(String url, InputStream inputStream, String contentType, long contentLength, String attachmentName)
782
            throws SmartsheetException {
783
        Util.throwIfNull(inputStream, contentType);
1✔
784
        HttpRequest request = createHttpRequest(this.getSmartsheet().getBaseURI().resolve(url), HttpMethod.POST);
1✔
785
        request.getHeaders().put(
1✔
786
                "Content-Disposition",
787
                "attachment; filename=\"" + URLEncoder.encode(attachmentName, StandardCharsets.UTF_8) + "\""
1✔
788
        );
789
        HttpEntity entity = new HttpEntity();
1✔
790
        entity.setContentType(contentType);
1✔
791
        entity.setContent(new LengthEnforcingInputStream(inputStream, contentLength));
1✔
792
        entity.setContentLength(contentLength);
1✔
793
        request.setEntity(entity);
1✔
794

795
        Attachment attachment = null;
1✔
796
        try {
797
            HttpResponse response = this.getSmartsheet().getHttpClient().request(request);
1✔
798
            switch (response.getStatusCode()) {
1✔
799
                case 200:
800
                    attachment = this.getSmartsheet().getJsonSerializer().deserializeResult(Attachment.class,
1✔
801
                            response.getEntity().getContent()).getResult();
1✔
802
                    break;
1✔
803
                default:
804
                    handleError(response);
×
805
            }
806
        } finally {
807
            this.getSmartsheet().getHttpClient().releaseConnection();
1✔
808
        }
809

810
        return attachment;
1✔
811
    }
812

813
    /**
814
     * Create a multipart upload request.
815
     *
816
     * @param url         the url
817
     * @param t           the object to create
818
     * @param partName    the name of the part
819
     * @param inputstream the file inputstream
820
     * @param contentType the type of the file to be attached
821
     * @return the http request
822
     * @throws SmartsheetException may be thrown in the method
823
     */
824
    public <T> Attachment attachFile(String url, T t, String partName, InputStream inputstream, String contentType, String attachmentName)
825
            throws SmartsheetException {
826
        Util.throwIfNull(inputstream, contentType);
×
827
        Attachment attachment = null;
×
828
        final String boundary = "----" + System.currentTimeMillis();
×
829

830
        CloseableHttpClient httpClient = HttpClients.createDefault();
×
831
        HttpPost uploadFile = createHttpPost(this.getSmartsheet().getBaseURI().resolve(url));
×
832

833
        try {
834
            uploadFile.setHeader(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
×
835
        } catch (Exception e) {
×
836
            throw new RuntimeException(e);
×
837
        }
×
838

839
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
×
840
        builder.setBoundary(boundary);
×
841
        builder.addTextBody(partName, this.getSmartsheet().getJsonSerializer().serialize(t), ContentType.APPLICATION_JSON);
×
842
        builder.addBinaryBody("file", inputstream, ContentType.create(contentType), attachmentName);
×
843
        org.apache.http.HttpEntity multipart = builder.build();
×
844

845
        uploadFile.setEntity(multipart);
×
846

847
        try {
848
            CloseableHttpResponse response = httpClient.execute(uploadFile);
×
849
            org.apache.http.HttpEntity responseEntity = response.getEntity();
×
850
            attachment = this.getSmartsheet().getJsonSerializer().deserializeResult(Attachment.class,
×
851
                    responseEntity.getContent()).getResult();
×
852
        } catch (Exception e) {
×
853
            throw new RuntimeException(e);
×
854
        }
×
855
        return attachment;
×
856
    }
857

858
    /**
859
     * Handles an error HttpResponse (non-200) returned by Smartsheet REST API.
860
     *
861
     * @param response the HttpResponse
862
     * @throws SmartsheetException     the smartsheet exception
863
     * @throws SmartsheetRestException : the exception corresponding to the error
864
     */
865
    protected void handleError(HttpResponse response) throws SmartsheetException {
866

867
        com.smartsheet.api.models.Error error;
868
        try {
869
            error = this.smartsheet.getJsonSerializer().deserialize(
1✔
870
                    com.smartsheet.api.models.Error.class, response.getEntity().getContent());
1✔
871
        } catch (JsonParseException e) {
×
872
            throw new SmartsheetException(e);
×
873
        } catch (JsonMappingException e) {
×
874
            throw new SmartsheetException(e);
×
875
        } catch (IOException e) {
×
876
            throw new SmartsheetException(e);
×
877
        }
1✔
878

879
        ErrorCode code = ErrorCode.getErrorCode(response.getStatusCode());
1✔
880

881
        if (code == null) {
1✔
882
            throw new SmartsheetRestException(error);
×
883
        }
884

885
        try {
886
            throw code.getException(error);
1✔
887
        } catch (IllegalArgumentException e) {
×
888
            throw new SmartsheetException(e);
×
889
        } catch (SecurityException e) {
×
890
            throw new SmartsheetException(e);
×
891
        }
892
    }
893

894
    /**
895
     * Gets the smartsheet.
896
     *
897
     * @return the smartsheet
898
     */
899
    public SmartsheetImpl getSmartsheet() {
900
        return smartsheet;
1✔
901
    }
902

903
    /**
904
     * Get a sheet as a file.
905
     *
906
     * @param path         the path
907
     * @param fileType     the output file type
908
     * @param outputStream the OutputStream to which the file will be written
909
     * @throws InvalidRequestException     : if there is any problem with the REST API request
910
     * @throws AuthorizationException      : if there is any problem with the REST API authorization(access token)
911
     * @throws ResourceNotFoundException   : if the resource can not be found
912
     * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
913
     * @throws SmartsheetRestException     : if there is any other REST API related error occurred during the operation
914
     * @throws SmartsheetException         : if there is any other error occurred during the operation
915
     */
916
    public void getResourceAsFile(String path, String fileType, OutputStream outputStream)
917
            throws SmartsheetException {
918
        Util.throwIfNull(outputStream, fileType);
1✔
919

920
        HttpRequest request;
921
        request = createHttpRequest(this.getSmartsheet().getBaseURI().resolve(path), HttpMethod.GET);
1✔
922
        request.getHeaders().put("Accept", fileType);
1✔
923

924
        try {
925
            HttpResponse response = getSmartsheet().getHttpClient().request(request);
1✔
926

927
            switch (response.getStatusCode()) {
1✔
928
                case 200:
929
                    try {
930
                        copyStream(response.getEntity().getContent(), outputStream);
1✔
931
                    } catch (IOException e) {
×
932
                        throw new SmartsheetException(e);
×
933
                    }
1✔
934
                    break;
935
                default:
936
                    handleError(response);
×
937
            }
938
        } finally {
939
            getSmartsheet().getHttpClient().releaseConnection();
1✔
940
        }
941
    }
1✔
942

943
    /*
944
     * Copy an input stream to an output stream.
945
     *
946
     * @param input The input stream to copy.
947
     *
948
     * @param output the output stream to write to.
949
     *
950
     * @throws IOException if there is trouble reading or writing to the streams.
951
     */
952

953
    /**
954
     * Copy stream.
955
     *
956
     * @param input  the input
957
     * @param output the output
958
     * @throws IOException Signals that an I/O exception has occurred.
959
     * @deprecated replace with StreamUtil.copyContentIntoOutputStream()
960
     */
961
    @Deprecated(since = "2.0.0", forRemoval = true)
962
    private static void copyStream(InputStream input, OutputStream output) throws IOException {
963
        byte[] buffer = new byte[BUFFER_SIZE];
1✔
964
        int len;
965
        while ((len = input.read(buffer)) != -1) {
1✔
966
            output.write(buffer, 0, len);
1✔
967
        }
968
    }
1✔
969

970
    /**
971
     * @return a map of headers to be used when making requests.
972
     */
973
    Map<String, String> createHeaders() {
974
        Map<String, String> headers = new HashMap<>();
1✔
975
        headers.put("Authorization", "Bearer " + smartsheet.getAccessToken());
1✔
976
        headers.put(HEADER_CONTENT_TYPE, JSON_CONTENT_TYPE);
1✔
977

978
        // Set assumed user
979
        if (smartsheet.getAssumedUser() != null) {
1✔
980
            headers.put("Assume-User", URLEncoder.encode(smartsheet.getAssumedUser(), StandardCharsets.UTF_8));
×
981
        }
982
        if (smartsheet.getChangeAgent() != null) {
1✔
983
            headers.put("Smartsheet-Change-Agent", URLEncoder.encode(smartsheet.getChangeAgent(), StandardCharsets.UTF_8));
1✔
984
        }
985
        if (smartsheet.getUserAgent() != null) {
1✔
986
            headers.put("User-Agent", smartsheet.getUserAgent());
1✔
987
        }
988
        return headers;
1✔
989
    }
990

991
    int getResponseLogLength() {
992
        // not cached to allow for it to be changed dynamically by client code
993
        return Integer.getInteger(PROPERTY_RESPONSE_LOG_CHARS, 1024);
1✔
994
    }
995
}
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

© 2025 Coveralls, Inc