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

smartsheet / smartsheet-java-sdk / #64

27 May 2026 12:40PM UTC coverage: 59.947% (-0.1%) from 60.09%
#64

push

github

web-flow
Add claude skills, docs and improved reports tests. (#172)

* Add claude skills, docs and improved reports tests.

* Fix tests and lint issues.

4544 of 7580 relevant lines covered (59.95%)

0.6 hits per line

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

42.67
/src/main/java/com/smartsheet/api/internal/ReportResourcesImpl.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.AuthorizationException;
20
import com.smartsheet.api.InvalidRequestException;
21
import com.smartsheet.api.ReportResources;
22
import com.smartsheet.api.ResourceNotFoundException;
23
import com.smartsheet.api.ServiceUnavailableException;
24
import com.smartsheet.api.ShareResources;
25
import com.smartsheet.api.SmartsheetException;
26
import com.smartsheet.api.internal.http.HttpEntity;
27
import com.smartsheet.api.internal.http.HttpMethod;
28
import com.smartsheet.api.internal.http.HttpRequest;
29
import com.smartsheet.api.internal.http.HttpResponse;
30
import com.smartsheet.api.internal.json.JSONSerializerException;
31
import com.smartsheet.api.internal.util.QueryUtil;
32
import com.smartsheet.api.models.CreateReportRequest;
33
import com.smartsheet.api.models.CreateReportResult;
34
import com.smartsheet.api.models.PagedResult;
35
import com.smartsheet.api.models.PaginationParameters;
36
import com.smartsheet.api.models.Report;
37
import com.smartsheet.api.models.ReportColumn;
38
import com.smartsheet.api.models.ReportDefinition;
39
import com.smartsheet.api.models.ReportPublish;
40
import com.smartsheet.api.models.Result;
41
import com.smartsheet.api.models.ReportScopeInclusion;
42
import com.smartsheet.api.models.SheetEmail;
43
import com.smartsheet.api.internal.util.Util;
44
import com.smartsheet.api.models.enums.ReportInclusion;
45

46
import java.io.ByteArrayInputStream;
47
import java.io.ByteArrayOutputStream;
48
import java.io.OutputStream;
49
import java.text.SimpleDateFormat;
50
import java.util.Date;
51
import java.util.EnumSet;
52
import java.util.HashMap;
53
import java.util.Map;
54
import java.util.List;
55

56
/**
57
 * This is the implementation of the ReportResources.
58
 * <p>
59
 * Thread Safety: This class is thread safe because it is immutable and its base class is thread safe.
60
 */
61

62
public class ReportResourcesImpl extends AbstractResources implements ReportResources {
63

64
    /**
65
     * Represents the ShareResources.
66
     * <p>
67
     * It will be initialized in constructor and will not change afterwards.
68
     */
69
    private ShareResources shares;
70

71
    private static final String JSON_CONTENT_TYPE = "application/json";
72
    private static final String REPORTS_PATH = "reports/";
73
    private static final String REPORTS = "reports";
74

75
    /**
76
     * Constructor.
77
     * <p>
78
     * Parameters: - smartsheet : the SmartsheetImpl
79
     * <p>
80
     * Exceptions: - IllegalArgumentException : if any argument is null
81
     *
82
     * @param smartsheet the smartsheet
83
     */
84
    public ReportResourcesImpl(SmartsheetImpl smartsheet) {
85
        super(smartsheet);
1✔
86
        this.shares = new ShareResourcesImpl(smartsheet, REPORTS);
1✔
87
    }
1✔
88

89
    /**
90
     * Get a report.
91
     * <p>
92
     * It mirrors to the following Smartsheet REST API method: GET /reports/{id}
93
     * <p>
94
     * Exceptions:
95
     * InvalidRequestException : if there is any problem with the REST API request
96
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
97
     * ResourceNotFoundException : if the resource can not be found
98
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
99
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
100
     * SmartsheetException : if there is any other error occurred during the operation
101
     *
102
     * @param reportId the folder id
103
     * @param includes the optional objects to include in response
104
     * @param pageSize Number of rows per page
105
     * @param page     page number to return
106
     * @return the report (note that if there is no such resource, this method will throw ResourceNotFoundException
107
     * rather than returning null)
108
     * @throws SmartsheetException the smartsheet exception
109
     */
110
    public Report getReport(long reportId, EnumSet<ReportInclusion> includes, Integer pageSize, Integer page) throws SmartsheetException {
111
        return this.getReport(reportId, includes, pageSize, page, null);
1✔
112
    }
113

114
    /**
115
     * Get a report.
116
     * <p>
117
     * It mirrors to the following Smartsheet REST API method: GET /reports/{id}
118
     * <p>
119
     * Exceptions:
120
     * InvalidRequestException : if there is any problem with the REST API request
121
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
122
     * ResourceNotFoundException : if the resource can not be found
123
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
124
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
125
     * SmartsheetException : if there is any other error occurred during the operation
126
     *
127
     * @param reportId the folder id
128
     * @param includes the optional objects to include in response
129
     * @param pageSize Number of rows per page
130
     * @param page     page number to return
131
     * @param level    compatibility level
132
     * @return the report (note that if there is no such resource, this method will throw ResourceNotFoundException
133
     * rather than returning null)
134
     * @throws SmartsheetException the smartsheet exception
135
     */
136
    public Report getReport(
137
            long reportId,
138
            EnumSet<ReportInclusion> includes,
139
            Integer pageSize,
140
            Integer page,
141
            Integer level
142
    ) throws SmartsheetException {
143
        String path = REPORTS_PATH + reportId;
1✔
144
        Map<String, Object> parameters = new HashMap<>();
1✔
145

146
        parameters.put("include", QueryUtil.generateCommaSeparatedList(includes));
1✔
147
        if (pageSize != null) {
1✔
148
            parameters.put("pageSize", pageSize.toString());
1✔
149
        }
150

151
        if (page != null) {
1✔
152
            parameters.put("page", page.toString());
1✔
153
        }
154

155
        if (level != null) {
1✔
156
            parameters.put("level", level);
×
157
        }
158

159
        path += QueryUtil.generateUrl(null, parameters);
1✔
160
        return this.getResource(path, Report.class);
1✔
161
    }
162

163
    /**
164
     * Sends a report as a PDF attachment via email to the designated recipients.
165
     * <p>
166
     * It mirrors to the following Smartsheet REST API method: POST /reports/{id}/emails
167
     * <p>
168
     * Exceptions:
169
     * InvalidRequestException : if there is any problem with the REST API request
170
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
171
     * ResourceNotFoundException : if the resource can not be found
172
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
173
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
174
     * SmartsheetException : if there is any other error occurred during the operation
175
     *
176
     * @param reportId the report id
177
     * @param email    the recipient email
178
     * @throws SmartsheetException the smartsheet exception
179
     */
180
    public void sendReport(long reportId, SheetEmail email) throws SmartsheetException {
181
        this.createResource(REPORTS_PATH + reportId + "/emails", SheetEmail.class, email);
1✔
182
    }
1✔
183

184
    /**
185
     * List all reports.
186
     * <p>
187
     * It mirrors to the following Smartsheet REST API method: GET /reports
188
     * <p>
189
     * Exceptions:
190
     * - InvalidRequestException : if there is any problem with the REST API request
191
     * - AuthorizationException : if there is any problem with the REST API authorization(access token)
192
     * - ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
193
     * - SmartsheetRestException : if there is any other REST API related error occurred during the operation
194
     * - SmartsheetException : if there is any other error occurred during the operation
195
     *
196
     * @param pagination pagination parameters for paging result
197
     * @return all sheets (note that empty list will be returned if there is none)
198
     * @throws SmartsheetException the smartsheet exception
199
     */
200
    public PagedResult<Report> listReports(PaginationParameters pagination) throws SmartsheetException {
201
        return this.listReports(pagination, null);
×
202
    }
203

204
    /**
205
     * List all reports.
206
     */
207
    public PagedResult<Report> listReports(PaginationParameters pagination, Date modifiedSince) throws SmartsheetException {
208
        String path = REPORTS;
1✔
209

210
        Map<String, Object> parameters = new HashMap<>();
1✔
211
        if (pagination != null) {
1✔
212
            parameters = pagination.toHashMap();
1✔
213
        }
214
        if (modifiedSince != null) {
1✔
215
            String isoDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(modifiedSince);
×
216
            parameters.put("modifiedSince", isoDate);
×
217
        }
218

219
        path += QueryUtil.generateUrl(null, parameters);
1✔
220
        return this.listResourcesWithWrapper(path, Report.class);
1✔
221
    }
222

223
    /**
224
     * Get a Report as an Excel file.
225
     * <p>
226
     * It mirrors to the following Smartsheet REST API method: GET /reports/{id} with "application/vnd.ms-excel" Accept
227
     * HTTP header
228
     * <p>
229
     * Exceptions:
230
     * IllegalArgumentException : if outputStream is null
231
     * InvalidRequestException : if there is any problem with the REST API request
232
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
233
     * ResourceNotFoundException : if the resource can not be found
234
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
235
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
236
     * SmartsheetException : if there is any other error occurred during the operation
237
     *
238
     * @param id           the id
239
     * @param outputStream the OutputStream to which the Excel file will be written
240
     * @throws SmartsheetException the smartsheet exception
241
     */
242
    public void getReportAsExcel(long id, OutputStream outputStream) throws SmartsheetException {
243
        getResourceAsFile(REPORTS_PATH + id, "application/vnd.ms-excel", outputStream);
1✔
244
    }
1✔
245

246
    /**
247
     * Get a Report as a csv file.
248
     * <p>
249
     * It mirrors to the following Smartsheet REST API method: GET /reports/{id} with "text/csv" Accept
250
     * HTTP header
251
     * <p>
252
     * Exceptions:
253
     * IllegalArgumentException : if outputStream is null
254
     * InvalidRequestException : if there is any problem with the REST API request
255
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
256
     * ResourceNotFoundException : if the resource can not be found
257
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
258
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
259
     * SmartsheetException : if there is any other error occurred during the operation
260
     *
261
     * @param id           the id
262
     * @param outputStream the OutputStream to which the Excel file will be written
263
     * @throws SmartsheetException the smartsheet exception
264
     */
265
    public void getReportAsCsv(long id, OutputStream outputStream) throws SmartsheetException {
266
        getResourceAsFile(REPORTS_PATH + id, "text/csv", outputStream);
×
267
    }
×
268

269
    /**
270
     * Get the publish status of a report.
271
     * <p>
272
     * It mirrors to the following Smartsheet REST API method: GET /reports/{id}/publish
273
     * <p>
274
     * Exceptions:
275
     * InvalidRequestException : if there is any problem with the REST API request
276
     * AuthorizationException : if there is any problem with the REST API authorization(access token)
277
     * ResourceNotFoundException : if the resource can not be found
278
     * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
279
     * SmartsheetRestException : if there is any other REST API related error occurred during the operation
280
     * SmartsheetException : if there is any other error occurred during the operation
281
     *
282
     * @param id the ID of the report
283
     * @return the report publish status (note that if there is no such resource, this method will
284
     * throw ResourceNotFoundException rather than returning null).
285
     * @throws IllegalArgumentException    if any argument is null or empty string
286
     * @throws InvalidRequestException     if there is any problem with the REST API request
287
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
288
     * @throws ResourceNotFoundException   if the resource cannot be found
289
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
290
     * @throws SmartsheetException         if there is any other error during the operation
291
     */
292
    public ReportPublish getPublishStatus(long id) throws SmartsheetException {
293
        return this.getResource(REPORTS_PATH + id + "/publish", ReportPublish.class);
×
294
    }
295

296
    /**
297
     * Sets the publish status of a report and returns the new status, including the URLs of any
298
     * enabled publishing.
299
     * <p>
300
     * It mirrors to the following Smartsheet REST API method: PUT /reports/{id}/publish
301
     * <p>
302
     * Exceptions:
303
     * - InvalidRequestException : if there is any problem with the REST API request
304
     * - AuthorizationException : if there is any problem with the REST API authorization(access token)
305
     * - ResourceNotFoundException : if the resource can not be found
306
     * - ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
307
     * - SmartsheetRestException : if there is any other REST API related error occurred during the operation
308
     * - SmartsheetException : if there is any other error occurred during the operation
309
     *
310
     * @param id            the ID of the report
311
     * @param reportPublish the ReportPublish object
312
     * @return the updated ReportPublish (note that if there is no such resource, this method will
313
     * throw ResourceNotFoundException rather than returning null)
314
     * @throws IllegalArgumentException    if any argument is null or empty string
315
     * @throws InvalidRequestException     if there is any problem with the REST API request
316
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
317
     * @throws ResourceNotFoundException   if the resource cannot be found
318
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
319
     * @throws SmartsheetException         if there is any other error during the operation
320
     */
321
    public ReportPublish updatePublishStatus(long id, ReportPublish reportPublish) throws SmartsheetException {
322
        return this.updateResource(REPORTS_PATH + id + "/publish", ReportPublish.class, reportPublish);
×
323
    }
324

325
    /**
326
     * Updates a report's definition (filters, grouping, summarizing, and sorting).
327
     * <p>
328
     * It mirrors to the following Smartsheet REST API method: PUT /reports/{id}/definition
329
     * <p>
330
     * This endpoint supports partial updates only on root level properties of the report definition,
331
     * such as filters, groupingCriteria, and summarizingCriteria. For example, you can update the
332
     * report's filters without affecting its grouping criteria. However, nested properties within
333
     * these objects, such as a specific filter or grouping criterion, cannot be updated individually
334
     * and require a full replacement of the respective section.
335
     * <p>
336
     * Exceptions:
337
     * - InvalidRequestException : if there is any problem with the REST API request
338
     * - AuthorizationException : if there is any problem with the REST API authorization(access token)
339
     * - ResourceNotFoundException : if the resource can not be found
340
     * - ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting)
341
     * - SmartsheetRestException : if there is any other REST API related error occurred during the operation
342
     * - SmartsheetException : if there is any other error occurred during the operation
343
     *
344
     * @param id         the ID of the report
345
     * @param reportDefinition the ReportDefinition object containing the updated definition
346
     * @throws IllegalArgumentException    if any argument is null
347
     * @throws InvalidRequestException     if there is any problem with the REST API request
348
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
349
     * @throws ResourceNotFoundException   if the resource cannot be found
350
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
351
     * @throws SmartsheetException         if there is any other error during the operation
352
     */
353
    public void updateReportDefinition(long id, ReportDefinition reportDefinition) throws SmartsheetException {
354
        String path = REPORTS_PATH + id + "/definition";
×
355
        this.putResource(path, Result.class, reportDefinition);
×
356
    }
×
357

358
    /**
359
     * <p>Creates an object of ShareResources.</p>
360
     *
361
     * @return the created ShareResources object
362
     */
363
    public ShareResources shareResources() {
364
        return this.shares;
×
365
    }
366

367
    /**
368
     * <p>Deletes a report.</p>
369
     *
370
     * <p>Mirrors the following Smartsheet REST API method: DELETE /reports/{reportId}</p>
371
     *
372
     * @param id the id of the report
373
     * @throws IllegalArgumentException    if any argument is null or empty string
374
     * @throws InvalidRequestException     if there is any problem with the REST API request
375
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
376
     * @throws ResourceNotFoundException   if the resource cannot be found
377
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
378
     * @throws SmartsheetException         if there is any other error during the operation
379
     */
380
    public void deleteReport(long id) throws SmartsheetException {
381
        this.deleteResource(REPORTS_PATH + id, Report.class);
1✔
382
    }
1✔
383

384
    /**
385
     * <p>Adds one or more specified sheet or workspace to the report scope.</p>
386
     *
387
     * @param id          the ID of the report
388
     * @param scopes A list of one or more objects denoting the sheets or workspaces associated with
389
     *               the report to be added to the report scope.
390
     * @throws IllegalArgumentException    if any argument is null or empty
391
     * @throws InvalidRequestException     if there is any problem with the REST API request
392
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
393
     * @throws ResourceNotFoundException   if the resource cannot be found
394
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
395
     * @throws SmartsheetException         if there is any other error during the operation
396
     */
397
    @Override
398
    public void addReportScope(long id, List<ReportScopeInclusion> scopes) throws SmartsheetException {
399
        Util.throwIfNull(scopes);
×
400

401
        if (scopes.isEmpty()) {
×
402
            throw new IllegalArgumentException("scopes should not be empty.");
×
403
        }
404

405
        String path = REPORTS_PATH + id + "/scope";
×
406
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST);
×
407
        setRequestEntity(request, scopes);
×
408

409
        try {
410
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
×
411
            if (response.getStatusCode() != 200) {
×
412
                handleError(response);
×
413
            }
414
        } finally {
415
            smartsheet.getHttpClient().releaseConnection();
×
416
        }
417
    }
×
418

419
    /**
420
     * <p>Removes one or more specified sheet or workspace from the report scope.</p>
421
     *
422
     * @param id             the ID of the report
423
     * @param scopes A list of one or more objects denoting the sheets or workspaces associated with
424
     *               the report to be removed from the report scope.
425
     * @throws IllegalArgumentException    if any argument is null or empty
426
     * @throws InvalidRequestException     if there is any problem with the REST API request
427
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
428
     * @throws ResourceNotFoundException   if the resource cannot be found
429
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
430
     * @throws SmartsheetException         if there is any other error during the operation
431
     */
432
    @Override
433
    public void removeReportScope(long id, List<ReportScopeInclusion> scopes) throws SmartsheetException {
434
        Util.throwIfNull(scopes);
×
435

436
        if (scopes.isEmpty()) {
×
437
            throw new IllegalArgumentException("scopes should not be empty.");
×
438
        }
439

440
        String path = REPORTS_PATH + id + "/scope";
×
441
        HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.DELETE);
×
442
        setRequestEntity(request, scopes);
×
443

444
        try {
445
            HttpResponse response = this.smartsheet.getHttpClient().request(request);
×
446
            if (response.getStatusCode() != 200) {
×
447
                handleError(response);
×
448
            }
449
        } finally {
450
            smartsheet.getHttpClient().releaseConnection();
×
451
        }
452
    }
×
453

454
    /**
455
     * <p>Add reportColumns to a report.</p>
456
     *
457
     * <p>It mirrors to the following Smartsheet REST API method: POST /reports/{reportId}/reportColumns</p>
458
     *
459
     * <p>Note: All indexes of the reportColumns must be equal.</p>
460
     *
461
     * @param reportId the ID of the report
462
     * @param reportColumns  the list of reportColumns to add (must contain 1-400 items)
463
     * @return the list of reportColumns that were added
464
     * @throws IllegalArgumentException    if any argument is null or empty
465
     * @throws InvalidRequestException     if there is any problem with the REST API request
466
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
467
     * @throws ResourceNotFoundException   if the resource cannot be found
468
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
469
     * @throws SmartsheetException         if there is any other error during the operation
470
     */
471
    @Override
472
    public List<ReportColumn> addReportColumns(long reportId, List<ReportColumn> reportColumns) throws SmartsheetException {
473
        Util.throwIfNull(reportColumns);
1✔
474

475
        if (reportColumns.isEmpty()) {
1✔
476
            throw new IllegalArgumentException("reportColumns should not be empty.");
×
477
        }
478

479
        return this.postAndReceiveList(REPORTS_PATH + reportId + "/columns", reportColumns, ReportColumn.class);
1✔
480
    }
481

482
    /**
483
     * <p>Create a new report.</p>
484
     *
485
     * <p>It mirrors to the following Smartsheet REST API method: POST /reports</p>
486
     *
487
     * <p>Creates a new report by specifying name, destination, scope, columns and definition.</p>
488
     *
489
     * @param request the CreateReportRequest containing report specifications
490
     * @return the CreateReportResult containing the newly created report information
491
     * @throws IllegalArgumentException    if any argument is null
492
     * @throws InvalidRequestException     if there is any problem with the REST API request
493
     * @throws AuthorizationException      if there is any problem with  the REST API authorization (access token)
494
     * @throws ServiceUnavailableException if the REST API service is not available (possibly due to rate limiting)
495
     * @throws SmartsheetException         if there is any other error during the operation
496
     */
497
    @Override
498
    public CreateReportResult createReport(CreateReportRequest request) throws SmartsheetException {
499
        Util.throwIfNull(request);
1✔
500

501
        return this.createResource(REPORTS, CreateReportResult.class, request);
1✔
502
    }
503

504
    private void setRequestEntity(HttpRequest request, Object object) throws JSONSerializerException {
505
        ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream();
×
506
        this.smartsheet.getJsonSerializer().serialize(object, objectBytesStream);
×
507
        HttpEntity entity = new HttpEntity();
×
508
        entity.setContentType(JSON_CONTENT_TYPE);
×
509
        entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray()));
×
510
        entity.setContentLength(objectBytesStream.size());
×
511
        request.setEntity(entity);
×
512
    }
×
513
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc