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

smartsheet / smartsheet-java-sdk / #58

23 Sep 2025 06:13PM UTC coverage: 59.737% (-1.1%) from 60.803%
#58

push

github

web-flow
Add user downgrade seat type endpoint (#131)

* Add support for upgrade user seat type route

* Change return type

* Add support for the user downgrade seat type endpoint

* Remove * imports, replace switch with if statement, remove overload

* Update changelog and build.gradle

* Add user downgrade and upgrade API calls

* Add user downgrade and upgrade API calls

* Add user downgrade and upgrade API calls

* Add user downgrade and upgrade API calls

---------

Co-authored-by: Velihan Zelev <velihan.zelev@smartsheet.com>

12 of 13 new or added lines in 2 files covered. (92.31%)

192 existing lines in 7 files now uncovered.

4310 of 7215 relevant lines covered (59.74%)

0.6 hits per line

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

69.93
/src/main/java/com/smartsheet/api/internal/SmartsheetImpl.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.smartsheet.api.AssetShareResources;
20
import com.smartsheet.api.ContactResources;
21
import com.smartsheet.api.EventResources;
22
import com.smartsheet.api.FavoriteResources;
23
import com.smartsheet.api.FolderResources;
24
import com.smartsheet.api.GroupResources;
25
import com.smartsheet.api.HomeResources;
26
import com.smartsheet.api.ImageUrlResources;
27
import com.smartsheet.api.PassthroughResources;
28
import com.smartsheet.api.ReportResources;
29
import com.smartsheet.api.SearchResources;
30
import com.smartsheet.api.ServerInfoResources;
31
import com.smartsheet.api.SheetResources;
32
import com.smartsheet.api.SightResources;
33
import com.smartsheet.api.Smartsheet;
34
import com.smartsheet.api.TemplateResources;
35
import com.smartsheet.api.TokenResources;
36
import com.smartsheet.api.Trace;
37
import com.smartsheet.api.UserResources;
38
import com.smartsheet.api.WebhookResources;
39
import com.smartsheet.api.WorkspaceResources;
40
import com.smartsheet.api.internal.http.AndroidHttpClient;
41
import com.smartsheet.api.internal.http.DefaultHttpClient;
42
import com.smartsheet.api.internal.http.HttpClient;
43
import com.smartsheet.api.internal.json.JacksonJsonSerializer;
44
import com.smartsheet.api.internal.json.JsonSerializer;
45
import com.smartsheet.api.internal.util.CleanerUtil;
46
import com.smartsheet.api.internal.util.Util;
47
import org.apache.http.impl.client.HttpClients;
48

49
import java.io.IOException;
50
import java.net.URI;
51
import java.net.URL;
52
import java.util.Properties;
53
import java.util.concurrent.atomic.AtomicReference;
54

55
/**
56
 * This is the implementation of Smartsheet interface.
57
 * <p>
58
 * Thread Safety: This class is thread safe because all its mutable fields are safe-guarded using AtomicReference to
59
 * ensure atomic modifications, and also the underlying HttpClient and JsonSerializer interfaces are thread safe.
60
 */
61
public class SmartsheetImpl implements Smartsheet {
62

63
    /**
64
     * Represents the base URI of the Smartsheet REST API.
65
     * <p>
66
     * It will be initialized in constructor and will not change afterward.
67
     */
68
    private final URI baseURI;
69

70
    /**
71
     * Represents the AtomicReference for access token.
72
     * <p>
73
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
74
     * as null, and can be set via corresponding setter, therefore effectively the access token can be updated in the
75
     * SmartsheetImpl in thread safe manner.
76
     */
77
    private final AtomicReference<String> accessToken;
78

79
    /**
80
     * Represents the HttpClient.
81
     * <p>
82
     * It will be initialized in constructor and will not change afterward.
83
     */
84
    private final HttpClient httpClient;
85

86
    /**
87
     * Represents the JsonSerializer.
88
     * <p>
89
     * It will be initialized in constructor and will not change afterward.
90
     */
91
    private final JsonSerializer jsonSerializer;
92

93
    /**
94
     * Represents the AtomicReference for assumed user email.
95
     * <p>
96
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
97
     * as null, and can be set via corresponding setter, therefore effectively the assumed user can be updated in the
98
     * SmartsheetImpl in thread safe manner.
99
     */
100
    private final AtomicReference<String> assumedUser;
101

102
    /**
103
     * Represents the AtomicReference for change agent
104
     * <p>
105
     * It will be initialized in constructor and will not change afterward.
106
     */
107
    private final AtomicReference<String> changeAgent;
108

109
    /**
110
     * Represents the AtomicReference for the user agent
111
     */
112
    private final AtomicReference<String> userAgent;
113

114
    /**
115
     * Represents the AtomicReference to HomeResources.
116
     * <p>
117
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
118
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
119
     * effectively the underlying value is lazily created in a thread safe manner.
120
     */
121
    private final AtomicReference<HomeResources> home;
122

123
    /**
124
     * Represents the AtomicReference to WorkspaceResources.
125
     * <p>
126
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
127
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
128
     * effectively the underlying value is lazily created in a thread safe manner.
129
     */
130
    private final AtomicReference<WorkspaceResources> workspaces;
131

132
    /**
133
     * Represents the AtomicReference to FolderResources.
134
     * <p>
135
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
136
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
137
     * effectively the underlying value is lazily created in a thread safe manner.
138
     */
139
    private final AtomicReference<FolderResources> folders;
140

141
    /**
142
     * Represents the AtomicReference to TemplateResources.
143
     * <p>
144
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
145
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
146
     * effectively the underlying value is lazily created in a thread safe manner.
147
     */
148
    private final AtomicReference<TemplateResources> templates;
149

150
    /**
151
     * Represents the AtomicReference to SheetResources.
152
     * <p>
153
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
154
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
155
     * effectively the underlying value is lazily created in a thread safe manner.
156
     */
157
    private final AtomicReference<SheetResources> sheets;
158

159
    /**
160
     * Represents the AtomicReference to SightResources
161
     * <p>
162
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
163
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
164
     * effectively the underlying value is lazily created in a thread safe manner.
165
     */
166
    private final AtomicReference<SightResources> sights;
167

168
    /**
169
     * Represents the AtomicReference to UserResources.
170
     * <p>
171
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
172
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
173
     * effectively the underlying value is lazily created in a thread safe manner.
174
     */
175
    private final AtomicReference<UserResources> users;
176

177
    /**
178
     * Represents the AtomicReference to {@link GroupResources}.
179
     * <p>
180
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
181
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
182
     * effectively the underlying value is lazily created in a thread safe manner.
183
     */
184
    private final AtomicReference<GroupResources> groups;
185

186
    /**
187
     * Represents the AtomicReference to SearchResources.
188
     * <p>
189
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
190
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
191
     * effectively the underlying value is lazily created in a thread safe manner.
192
     */
193
    private final AtomicReference<SearchResources> search;
194

195
    /**
196
     * Represents the AtomicReference to ReportResources.
197
     * <p>
198
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
199
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
200
     * effectively the underlying value is lazily created in a thread safe manner.
201
     */
202
    private final AtomicReference<ReportResources> reports;
203

204
    /**
205
     * Represents the AtomicReference for ServerInfoResources.
206
     * <p>
207
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
208
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
209
     * effectively the underlying value is lazily created in a thread safe manner.
210
     */
211
    private final AtomicReference<ServerInfoResources> serverInfo;
212

213
    /**
214
     * Represents the AtomicReference for FavoriteResources.
215
     * <p>
216
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
217
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
218
     * effectively the underlying value is lazily created in a thread safe manner.
219
     */
220
    private final AtomicReference<FavoriteResources> favorites;
221

222
    /**
223
     * Represents the AtomicReference for TokenResources.
224
     * <p>
225
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
226
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
227
     * effectively the underlying value is lazily created in a thread safe manner.
228
     */
229
    private final AtomicReference<TokenResources> tokens;
230

231
    /**
232
     * Represents the AtomicReference for ContactResources.
233
     * <p>
234
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
235
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
236
     * effectively the underlying value is lazily created in a thread safe manner.
237
     */
238
    private final AtomicReference<ContactResources> contacts;
239

240
    /**
241
     * Represents the AtomicReference for ImageUrlResources.
242
     * <p>
243
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
244
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
245
     * effectively the underlying value is lazily created in a thread safe manner.
246
     */
247
    private final AtomicReference<ImageUrlResources> imageUrls;
248

249
    /**
250
     * Represents the AtomicReference for WebhookResources.
251
     * <p>
252
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
253
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
254
     * effectively the underlying value is lazily created in a thread safe manner.
255
     */
256
    private final AtomicReference<WebhookResources> webhooks;
257

258
    /**
259
     * Represents the AtomicReference for PassthroughResources.
260
     * <p>
261
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
262
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
263
     * effectively the underlying value is lazily created in a thread safe manner.
264
     */
265
    private final AtomicReference<PassthroughResources> passthrough;
266

267
    /**
268
     * Represents the AtomicReference for EventResources.
269
     * <p>
270
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
271
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
272
     * effectively the underlying value is lazily created in a thread safe manner.
273
     */
274
    private final AtomicReference<EventResources> events;
275

276
    /**
277
     * Represents the AtomicReference for AssetShareResources.
278
     * <p>
279
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
280
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
281
     * effectively the underlying value is lazily created in a thread safe manner.
282
     */
283
    private final AtomicReference<AssetShareResources> assetShares;
284

285
    private static final String INVALID_OPERATION_FOR_CLASS = "Invalid operation for class ";
286

287
    /**
288
     * Create an instance with given server URI, HttpClient (optional) and JsonSerializer (optional)
289
     * <p>
290
     * Exceptions: - IllegalArgumentException : if serverURI/version/accessToken is null/empty
291
     *
292
     * @param baseURI     the server uri
293
     * @param accessToken the access token
294
     */
295
    public SmartsheetImpl(String baseURI, String accessToken) {
296
        this(baseURI, accessToken, null, null);
×
297
    }
×
298

299
    /**
300
     * Create an instance with given server URI, HttpClient (optional) and JsonSerializer (optional)
301
     * <p>
302
     * Exceptions: - IllegalArgumentException : if serverURI/version/accessToken is null/empty
303
     *
304
     * @param baseURI        the server uri
305
     * @param accessToken    the access token
306
     * @param httpClient     the http client (optional)
307
     * @param jsonSerializer the json serializer (optional)
308
     */
309
    public SmartsheetImpl(String baseURI, String accessToken, HttpClient httpClient, JsonSerializer jsonSerializer) {
1✔
310
        Util.throwIfNull(baseURI);
1✔
311
        Util.throwIfEmpty(baseURI);
1✔
312

313
        this.baseURI = URI.create(baseURI);
1✔
314
        this.accessToken = new AtomicReference<>(accessToken);
1✔
315
        this.jsonSerializer = (jsonSerializer == null) ? new JacksonJsonSerializer() : jsonSerializer;
1✔
316
        this.httpClient = (httpClient == null)
1✔
317
                ? new DefaultHttpClient(HttpClients.createDefault(), this.jsonSerializer) : httpClient;
1✔
318
        CleanerUtil.register(this, CleanerUtil.closeQuietly(this.httpClient));
1✔
319

320
        this.assumedUser = new AtomicReference<>(null);
1✔
321
        this.changeAgent = new AtomicReference<>(null);
1✔
322
        this.userAgent = new AtomicReference<>(generateUserAgent(null));
1✔
323

324
        // Initialize resources
325
        this.home = new AtomicReference<>();
1✔
326
        this.workspaces = new AtomicReference<>();
1✔
327
        this.folders = new AtomicReference<>();
1✔
328
        this.templates = new AtomicReference<>();
1✔
329
        this.sheets = new AtomicReference<>();
1✔
330
        this.sights = new AtomicReference<>();
1✔
331
        this.favorites = new AtomicReference<>();
1✔
332
        this.users = new AtomicReference<>();
1✔
333
        this.groups = new AtomicReference<>();
1✔
334
        this.search = new AtomicReference<>();
1✔
335
        this.reports = new AtomicReference<>();
1✔
336
        this.serverInfo = new AtomicReference<>();
1✔
337
        this.tokens = new AtomicReference<>();
1✔
338
        this.contacts = new AtomicReference<>();
1✔
339
        this.imageUrls = new AtomicReference<>();
1✔
340
        this.webhooks = new AtomicReference<>();
1✔
341
        this.passthrough = new AtomicReference<>();
1✔
342
        this.events = new AtomicReference<>();
1✔
343
        this.assetShares = new AtomicReference<>();
1✔
344
    }
1✔
345

346
    /**
347
     * Getter of corresponding field.
348
     * <p>
349
     * Returns: corresponding field.
350
     *
351
     * @return the base uri
352
     */
353
    URI getBaseURI() {
354
        return baseURI;
1✔
355
    }
356

357
    /**
358
     * Return the access token
359
     *
360
     * @return the access token
361
     */
362
    String getAccessToken() {
363
        return accessToken.get();
1✔
364
    }
365

366
    /**
367
     * Set the access token to use.
368
     * <p>
369
     * Parameters: - accessToken : the access token
370
     * <p>
371
     * Returns: None
372
     * <p>
373
     *
374
     * @param accessToken the new access token
375
     */
376
    public void setAccessToken(String accessToken) {
377
        this.accessToken.set(accessToken);
1✔
378
    }
1✔
379

380
    /**
381
     * Getter of corresponding field.
382
     *
383
     * @return corresponding field
384
     */
385
    JsonSerializer getJsonSerializer() {
386
        return jsonSerializer;
1✔
387
    }
388

389
    /**
390
     * Getter of corresponding field.
391
     *
392
     * @return corresponding field.
393
     */
394
    HttpClient getHttpClient() {
395
        return httpClient;
1✔
396
    }
397

398
    /**
399
     * Return the assumed user.
400
     *
401
     * @return the assumed user
402
     */
403
    String getAssumedUser() {
404
        return assumedUser.get();
1✔
405
    }
406

407
    /**
408
     * Set the email of the user to assume. Null/empty string indicates no user is assumed.
409
     *
410
     * @param assumedUser the email of the user to assume
411
     */
412
    public void setAssumedUser(String assumedUser) {
413
        this.assumedUser.set(assumedUser);
1✔
414
    }
1✔
415

416
    /**
417
     * Return the change agent identifier.
418
     *
419
     * @return the access token
420
     */
421
    String getChangeAgent() {
422
        return changeAgent.get();
1✔
423
    }
424

425
    /**
426
     * Sets the change agent identifier
427
     */
428
    public void setChangeAgent(String changeAgent) {
429
        this.changeAgent.set(changeAgent);
1✔
430
    }
1✔
431

432
    /**
433
     * Return the user agent string
434
     *
435
     * @return the user agent string
436
     */
437
    public String getUserAgent() {
438
        return userAgent.get();
1✔
439
    }
440

441
    /**
442
     * Sets the user agent string
443
     *
444
     * @param userAgent the user agent string
445
     */
446
    public void setUserAgent(String userAgent) {
447
        this.userAgent.set(generateUserAgent(userAgent));
×
448
    }
×
449

450
    /**
451
     * Sets the max retry time if the HttpClient is an instance of DefaultHttpClient
452
     *
453
     * @param maxRetryTimeMillis max retry time
454
     */
455
    public void setMaxRetryTimeMillis(long maxRetryTimeMillis) {
456
        if (this.httpClient instanceof DefaultHttpClient) {
×
457
            ((DefaultHttpClient) this.httpClient).setMaxRetryTimeMillis(maxRetryTimeMillis);
×
458
        } else if (this.httpClient instanceof AndroidHttpClient) {
×
459
            ((AndroidHttpClient) this.httpClient).setMaxRetryTimeMillis(maxRetryTimeMillis);
×
460
        } else {
461
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
462
        }
463
    }
×
464

465
    /**
466
     * set what request/response fields to log in trace-logging
467
     */
468
    public void setTraces(Trace... traces) {
469
        if (this.httpClient instanceof DefaultHttpClient) {
1✔
470
            ((DefaultHttpClient) this.httpClient).setTraces(traces);
1✔
471
        } else {
472
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
473
        }
474
    }
1✔
475

476
    /**
477
     * set whether or not to generate "pretty formatted" JSON in trace-logging
478
     */
479
    public void setTracePrettyPrint(boolean pretty) {
480
        if (this.httpClient instanceof DefaultHttpClient) {
×
481
            ((DefaultHttpClient) this.httpClient).setTracePrettyPrint(pretty);
×
482
        } else {
483
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
484
        }
485
    }
×
486

487
    /**
488
     * Returns the HomeResources instance that provides access to Home resources.
489
     *
490
     * @return the home resources
491
     * @deprecated Home resources have been deprecated and will be removed in a future version.
492
     */
493
    @Deprecated(since = "3.4.0", forRemoval = true)
494
    public HomeResources homeResources() {
495
        if (home.get() == null) {
1✔
496
            home.compareAndSet(null, new HomeResourcesImpl(this));
1✔
497
        }
498
        return home.get();
1✔
499
    }
500

501
    /**
502
     * Returns the WorkspaceResources instance that provides access to Workspace resources.
503
     *
504
     * @return the workspace resources
505
     */
506
    public WorkspaceResources workspaceResources() {
507
        if (workspaces.get() == null) {
1✔
508
            workspaces.compareAndSet(null, new WorkspaceResourcesImpl(this));
1✔
509
        }
510
        return workspaces.get();
1✔
511
    }
512

513
    /**
514
     * Returns the FolderResources instance that provides access to Folder resources.
515
     *
516
     * @return the folder resources
517
     */
518
    public FolderResources folderResources() {
519
        if (folders.get() == null) {
1✔
520
            folders.compareAndSet(null, new FolderResourcesImpl(this));
1✔
521
        }
522
        return folders.get();
1✔
523
    }
524

525
    /**
526
     * Returns the TemplateResources instance that provides access to Template resources.
527
     *
528
     * @return the template resources
529
     */
530
    public TemplateResources templateResources() {
531
        if (templates.get() == null) {
1✔
532
            templates.compareAndSet(null, new TemplateResourcesImpl(this));
1✔
533
        }
534
        return templates.get();
1✔
535
    }
536

537
    /**
538
     * Returns the SheetResources instance that provides access to Sheet resources.
539
     *
540
     * @return the sheet resources
541
     */
542
    public SheetResources sheetResources() {
543
        if (sheets.get() == null) {
1✔
544
            sheets.compareAndSet(null, new SheetResourcesImpl(this));
1✔
545
        }
546
        return sheets.get();
1✔
547
    }
548

549
    /**
550
     * Returns the SightResources instance that provides access to Sight resources.
551
     *
552
     * @return the sight resources
553
     */
554
    public SightResources sightResources() {
555
        if (sights.get() == null) {
1✔
556
            sights.compareAndSet(null, new SightResourcesImpl(this));
1✔
557
        }
558
        return sights.get();
1✔
559
    }
560

561
    /**
562
     * Returns the FavoriteResources instance that provides access to Favorite resources.
563
     *
564
     * @return the favorite resources
565
     */
566
    public FavoriteResources favoriteResources() {
567
        if (favorites.get() == null) {
1✔
568
            favorites.compareAndSet(null, new FavoriteResourcesImpl(this));
1✔
569
        }
570
        return favorites.get();
1✔
571
    }
572

573
    /**
574
     * Returns the {@link UserResources} instance that provides access to User resources.
575
     *
576
     * @return the user resources
577
     */
578
    public UserResources userResources() {
579
        if (users.get() == null) {
1✔
580
            users.compareAndSet(null, new UserResourcesImpl(this));
1✔
581
        }
582
        return users.get();
1✔
583
    }
584

585
    /**
586
     * Returns the {@link GroupResources} instance that provides access to User resources.
587
     *
588
     * @return the user resources
589
     */
590
    public GroupResources groupResources() {
UNCOV
591
        if (groups.get() == null) {
×
592
            groups.compareAndSet(null, new GroupResourcesImpl(this));
×
593
        }
UNCOV
594
        return groups.get();
×
595
    }
596

597
    /**
598
     * Returns the {@link SearchResources} instance that provides access to searching resources.
599
     *
600
     * @return the search resources
601
     */
602
    public SearchResources searchResources() {
603
        if (search.get() == null) {
1✔
604
            search.compareAndSet(null, new SearchResourcesImpl(this));
1✔
605
        }
606
        return search.get();
1✔
607
    }
608

609
    /**
610
     * Returns the {@link ReportResources} instance that provides access to Report resources.
611
     *
612
     * @return the report resources
613
     */
614
    public ReportResources reportResources() {
615
        if (reports.get() == null) {
1✔
616
            reports.compareAndSet(null, new ReportResourcesImpl(this));
1✔
617
        }
618
        return reports.get();
1✔
619
    }
620

621
    /**
622
     * Returns the {@link ServerInfoResources} instance that provides access to ServerInfo resources.
623
     *
624
     * @return the ServerInfo resources
625
     */
626
    public ServerInfoResources serverInfoResources() {
UNCOV
627
        if (serverInfo.get() == null) {
×
628
            serverInfo.compareAndSet(null, new ServerInfoResourcesImpl(this));
×
629
        }
UNCOV
630
        return serverInfo.get();
×
631
    }
632

633
    /**
634
     * Returns the TokenResources instance that provides access to token resources.
635
     *
636
     * @return the token resources
637
     */
638
    public TokenResources tokenResources() {
UNCOV
639
        if (tokens.get() == null) {
×
640
            tokens.compareAndSet(null, new TokenResourcesImpl(this));
×
641
        }
UNCOV
642
        return tokens.get();
×
643
    }
644

645
    /**
646
     * Returns the ContactResources instance that provides access to contact resources.
647
     *
648
     * @return the contact resources
649
     */
650
    public ContactResources contactResources() {
UNCOV
651
        if (contacts.get() == null) {
×
652
            contacts.compareAndSet(null, new ContactResourcesImpl(this));
×
653
        }
UNCOV
654
        return contacts.get();
×
655
    }
656

657
    /**
658
     * Returns the ImageUrlResources instance that provides access to image url resources.
659
     *
660
     * @return the image url resources
661
     */
662
    public ImageUrlResources imageUrlResources() {
UNCOV
663
        if (imageUrls.get() == null) {
×
664
            imageUrls.compareAndSet(null, new ImageUrlResourcesImpl(this));
×
665
        }
UNCOV
666
        return imageUrls.get();
×
667
    }
668

669
    /**
670
     * Returns the WebhookResources instance that provides access to webhook resources.
671
     *
672
     * @return the webhook resources
673
     */
674
    public WebhookResources webhookResources() {
UNCOV
675
        if (webhooks.get() == null) {
×
676
            webhooks.compareAndSet(null, new WebhookResourcesImpl(this));
×
677
        }
UNCOV
678
        return webhooks.get();
×
679
    }
680

681
    /**
682
     * Returns the PassthroughResources instance that provides access to passthrough resources.
683
     *
684
     * @return the passthrough resources
685
     */
686
    public PassthroughResources passthroughResources() {
UNCOV
687
        if (passthrough.get() == null) {
×
688
            passthrough.compareAndSet(null, new PassthroughResourcesImpl(this));
×
689
        }
UNCOV
690
        return passthrough.get();
×
691
    }
692

693
    /**
694
     * Returns the EventResources instance that provides access to events resources.
695
     *
696
     * @return the events resources
697
     */
698
    public EventResources eventResources() {
UNCOV
699
        if (events.get() == null) {
×
700
            events.compareAndSet(null, new EventResourcesImpl(this));
×
701
        }
UNCOV
702
        return events.get();
×
703
    }
704

705
    /**
706
     * Returns the AssetShareResources instance that provides access to Asset Share resources.
707
     *
708
     * @return the asset share resources
709
     */
710
    public AssetShareResources assetShareResources() {
UNCOV
711
        if (assetShares.get() == null) {
×
712
            assetShares.compareAndSet(null, new AssetShareResourcesImpl(this));
×
713
        }
UNCOV
714
        return assetShares.get();
×
715
    }
716

717
    /**
718
     * Compose a User-Agent string that represents this version of the SDK (along with platform info)
719
     *
720
     * @return a User-Agent string
721
     */
722
    private String generateUserAgent(String userAgent) {
723
        String title = null;
1✔
724
        String thisVersion = null;
1✔
725

726
        if (userAgent == null) {
1✔
727
            StackTraceElement[] callers = Thread.currentThread().getStackTrace();
1✔
728
            String module = null;
1✔
729
            String callerClass = null;
1✔
730
            int stackIdx;
731
            for (stackIdx = callers.length - 1; stackIdx >= 0; stackIdx--) {
1✔
732
                callerClass = callers[stackIdx].getClassName();
1✔
733
                try {
734
                    Class<?> clazz = Class.forName(callerClass);
1✔
735
                    ClassLoader classLoader = clazz.getClassLoader();
1✔
736
                    // skip JRE classes
737
                    if (classLoader == null) {
1✔
UNCOV
738
                        continue;
×
739
                    }
740
                    String classFilePath = callerClass.replace(".", "/") + ".class";
1✔
741
                    URL classUrl = classLoader.getResource(classFilePath);
1✔
742
                    if (classUrl != null) {
1✔
743
                        String classUrlPath = classUrl.getPath();
1✔
744
                        int jarSeparator = classUrlPath.indexOf('!');
1✔
745
                        if (jarSeparator > 0) {
1✔
746
                            module = classUrlPath.substring(0, jarSeparator);
1✔
747
                            // extract the last path element (the jar name only)
748
                            module = module.substring(module.lastIndexOf('/') + 1);
1✔
749
                            break;
1✔
750
                        }
751
                    }
752
                } catch (Exception ex) {
×
753
                    // Empty Catch Block
UNCOV
754
                }
×
755
            }
756
            userAgent = module + "!" + callerClass;
1✔
757
        }
758
        try {
759
            final Properties properties = new Properties();
1✔
760
            properties.load(this.getClass().getClassLoader().getResourceAsStream("sdk.properties"));
1✔
761
            thisVersion = properties.getProperty("sdk.version");
1✔
762
            title = properties.getProperty("sdk.name");
1✔
UNCOV
763
        } catch (IOException e) {
×
764
            // Empty Catch Block
765
        }
1✔
766
        return title + "/" + thisVersion + "/" + userAgent + "/" + System.getProperty("os.name") + " " +
1✔
767
                System.getProperty("java.vm.name") + " " + System.getProperty("java.vendor") + " " +
1✔
768
                System.getProperty("java.version");
1✔
769
    }
770
}
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