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

smartsheet / smartsheet-java-sdk / #44

25 Aug 2023 05:39PM UTC coverage: 50.55% (+0.1%) from 50.427%
#44

push

github-actions

web-flow
Fix remaining Checkstyle violations and Enable Checkstyle (#58)

* Fix remaining Checkstyle violations and Enable Checkstyle

We are now down to `20` checkstyle violations in main and `0` violations in test.

The remaining 20 violations are not trivial to fix, so I've set checkstyle to allow those 20 violations to exist, but to fail the build if we ever exceed 20 violations. This should make the build fail if any new violations are added.

For tests, we do not allow _any_ violations. This means adding a single violation will fail the build. Once the 20 violations in main are cleaned up, we can make main and test have the same config.

Note: This MR also changes our PR pipeline to run `./gradlew clean build` instead of `./gradlew clean test`. The reason for this is that build runs all the tests and performs all the other checks (such as checkstyle), whereas `test` didn't run checkstyle and we wouldn't have noticed violations until we tried to deploy.

148 of 148 new or added lines in 24 files covered. (100.0%)

3448 of 6821 relevant lines covered (50.55%)

0.51 hits per line

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

93.41
/src/main/java/com/smartsheet/api/internal/http/RequestAndResponseData.java
1
package com.smartsheet.api.internal.http;
2

3
/*
4
 * #[license]
5
 * Smartsheet Java SDK
6
 * %%
7
 * Copyright (C) 2023 Smartsheet
8
 * %%
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *      http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 * %[license]
21
 */
22

23
import com.smartsheet.api.Trace;
24
import org.apache.http.Header;
25
import org.apache.http.client.methods.HttpRequestBase;
26

27
import java.io.IOException;
28
import java.nio.charset.StandardCharsets;
29
import java.util.Map;
30
import java.util.Set;
31
import java.util.TreeMap;
32

33
/**
34
 * a POJO from which is generated JSON from HTTP request/response pairs
35
 */
36
public class RequestAndResponseData {
37
    public abstract static class HttpPayloadData {
1✔
38
        Map<String, String> headers;
39
        String body;
40

41
        public String getBody() {
42
            return body;
×
43
        }
44

45
        public boolean hasBody() {
46
            return body != null;
1✔
47
        }
48

49
        public boolean hasHeaders() {
50
            return headers != null;
1✔
51
        }
52

53
        public Map<String, String> getHeaders() {
54
            return headers;
1✔
55
        }
56

57
        abstract static class Builder<Type extends HttpPayloadData> {
1✔
58
            public void withHeaders() {
59
                // this is seaprate from addHeader in case headers were requested but none found
60
                if (getDataObject().headers == null) {
1✔
61
                    getDataObject().headers = new TreeMap<>();
1✔
62
                }
63
            }
1✔
64

65
            public Builder addHeader(String key, String val) {
66
                withHeaders();
1✔
67
                getDataObject().headers.put(key, val);
1✔
68
                return this;
1✔
69
            }
70

71
            public Builder setBody(String body) {
72
                getDataObject().body = body;
1✔
73
                return this;
1✔
74
            }
75

76
            public abstract Type build();
77

78
            public abstract void reset();
79

80
            protected abstract Type getDataObject();
81
        }
82
    }
83

84
    public static class RequestData extends HttpPayloadData {
1✔
85
        private String command;
86

87
        public String getCommand() {
88
            return command;
1✔
89
        }
90

91
        public static class Builder extends HttpPayloadData.Builder<RequestData> {
1✔
92
            private RequestData dataObject;
93

94
            @Override
95
            public void reset() {
96
                dataObject = null;
1✔
97
            }
1✔
98

99
            @Override
100
            protected RequestData getDataObject() {
101
                if (dataObject == null) {
1✔
102
                    dataObject = new RequestData();
1✔
103
                }
104
                return dataObject;
1✔
105
            }
106

107
            /**
108
             * Add a command
109
             */
110
            public HttpPayloadData.Builder withCommand(String command) {
111
                getDataObject().command = command;
1✔
112
                return this;
1✔
113
            }
114

115
            /**
116
             * Build the RequestData
117
             */
118
            public RequestData build() {
119
                try {
120
                    // if nothing was added then nothing was built (i.e., this can be null)
121
                    return dataObject;
1✔
122
                } finally {
123
                    reset();
1✔
124
                }
125
            }
126
        }
127
    }
128

129
    public static class ResponseData extends HttpPayloadData {
1✔
130
        private String status;
131

132
        public String getStatus() {
133
            return status;
1✔
134
        }
135

136
        public static class Builder extends HttpPayloadData.Builder<ResponseData> {
1✔
137
            private ResponseData dataObject;
138

139
            @Override
140
            public void reset() {
141
                dataObject = null;
1✔
142
            }
1✔
143

144
            @Override
145
            protected ResponseData getDataObject() {
146
                if (dataObject == null) {
1✔
147
                    dataObject = new ResponseData();
1✔
148
                }
149
                return dataObject;
1✔
150
            }
151

152
            /**
153
             * Add a status
154
             */
155
            public HttpPayloadData.Builder withStatus(String status) {
156
                getDataObject().status = status;
1✔
157
                return this;
1✔
158
            }
159

160
            /**
161
             * Build the ResponseData
162
             */
163
            public ResponseData build() {
164
                try {
165
                    // if nothing was added then nothing was built (i.e., this can be null)
166
                    return dataObject;
1✔
167
                } finally {
168
                    reset();
1✔
169
                }
170
            }
171
        }
172
    }
173

174
    private static int TRUNCATE_LENGTH = Integer.getInteger("Smartsheet.trace.truncateLen", 1024);
1✔
175
    private static final String NULL_STRING = "null";
176

177
    public final RequestData request;
178
    public final ResponseData response;
179

180
    private RequestAndResponseData(RequestData requestData, ResponseData responseData) {
1✔
181
        request = requestData;
1✔
182
        response = responseData;
1✔
183
    }
1✔
184

185
    @Override
186
    public String toString() {
187
        return toString(false);
1✔
188
    }
189

190
    /**
191
     * Convert to a String
192
     */
193
    public String toString(boolean pretty) {
194
        final String eol = pretty ? "\n" : "";
1✔
195
        final String indent = pretty ? "  " : "";
1✔
196
        final String doubleIndent = indent + indent;
1✔
197
        final String tripleIndent = doubleIndent + indent;
1✔
198

199
        StringBuilder buf = new StringBuilder();
1✔
200
        buf.append("{").append(eol);
1✔
201
        buf.append(indent).append("request:");
1✔
202
        if (request == null) {
1✔
203
            buf.append("null,").append(eol);
×
204
        } else {
205
            buf.append("{").append(eol);
1✔
206
            buf
1✔
207
                    .append(doubleIndent)
1✔
208
                    .append("command:'")
1✔
209
                    .append(request.getCommand())
1✔
210
                    .append("'")
1✔
211
                    .append(",")
1✔
212
                    .append(eol);
1✔
213
            if (request.hasHeaders()) {
1✔
214
                buf.append(doubleIndent).append("headers:");
1✔
215
                if (request.getHeaders() == null) {
1✔
216
                    buf.append(NULL_STRING);
×
217
                } else {
218
                    buf.append("{").append(eol);
1✔
219
                    for (Map.Entry<String, String> header : request.headers.entrySet()) {
1✔
220
                        buf
1✔
221
                                .append(tripleIndent)
1✔
222
                                .append("'")
1✔
223
                                .append(header.getKey())
1✔
224
                                .append("':'")
1✔
225
                                .append(header.getValue())
1✔
226
                                .append("'")
1✔
227
                                .append(",")
1✔
228
                                .append(eol);
1✔
229
                    }
1✔
230
                    buf.append(doubleIndent).append("}").append(",").append(eol);
1✔
231
                }
232
            }
233
            if (request.hasBody()) {
1✔
234
                buf.append(doubleIndent).append("body:");
1✔
235
                if (request.body == null) {
1✔
236
                    buf.append(NULL_STRING);
×
237
                } else {
238
                    buf.append("'").append(request.body).append("'");
1✔
239
                }
240
                buf.append(eol);
1✔
241
            }
242
            buf.append(indent).append("},").append(eol);
1✔
243
        }
244
        buf.append(indent).append("response:");
1✔
245
        if (response == null) {
1✔
246
            buf.append(NULL_STRING).append(eol);
×
247
        } else {
248
            buf.append("{").append(eol);
1✔
249
            buf.append(doubleIndent).append("status:'").append(response.getStatus()).append("',").append(eol);
1✔
250
            if (response.hasHeaders()) {
1✔
251
                buf.append(doubleIndent).append("headers:");
1✔
252
                if (response.getHeaders() == null) {
1✔
253
                    buf.append(NULL_STRING);
×
254
                } else {
255
                    buf.append("{").append(eol);
1✔
256
                    for (Map.Entry<String, String> header : response.headers.entrySet()) {
1✔
257
                        buf
1✔
258
                                .append(tripleIndent)
1✔
259
                                .append("'")
1✔
260
                                .append(header.getKey())
1✔
261
                                .append("':'")
1✔
262
                                .append(header.getValue())
1✔
263
                                .append("',").append(eol);
1✔
264
                    }
1✔
265
                    buf.append(doubleIndent).append("},").append(eol);
1✔
266
                }
267
            }
268
            if (response.hasBody()) {
1✔
269
                buf.append(doubleIndent).append("body:");
1✔
270
                if (response.body == null) {
1✔
271
                    buf.append(NULL_STRING);
×
272
                } else {
273
                    buf.append("'").append(response.body).append("'");
1✔
274
                }
275
                buf.append(eol);
1✔
276
            }
277
            buf.append(indent).append("}").append(eol);
1✔
278
        }
279
        buf.append("}");
1✔
280
        return buf.toString();
1✔
281
    }
282

283
    /**
284
     * factory method for creating a RequestAndResponseData object from request and response data with the specifid trace fields
285
     */
286
    public static RequestAndResponseData of(HttpRequestBase request, HttpEntitySnapshot requestEntity,
287
                                            HttpResponse response, HttpEntitySnapshot responseEntity,
288
                                            Set<Trace> traces)
289
            throws IOException {
290
        RequestData.Builder requestBuilder = new RequestData.Builder();
1✔
291
        ResponseData.Builder responseBuilder = new ResponseData.Builder();
1✔
292

293
        if (request != null) {
1✔
294
            requestBuilder.withCommand(request.getMethod() + " " + request.getURI());
1✔
295
            boolean binaryBody = false;
1✔
296
            if (traces.contains(Trace.RequestHeaders) && request.getAllHeaders() != null) {
1✔
297
                requestBuilder.withHeaders();
1✔
298
                for (Header header : request.getAllHeaders()) {
1✔
299
                    String headerName = header.getName();
1✔
300
                    String headerValue = header.getValue();
1✔
301
                    if ("Authorization".equals(headerName) && headerValue.length() > 0) {
1✔
302
                        headerValue = "Bearer ****" + headerValue.substring(Math.max(0, headerValue.length() - 4));
1✔
303
                    } else if ("Content-Disposition".equals(headerName)) {
1✔
304
                        binaryBody = true;
1✔
305
                    }
306
                    requestBuilder.addHeader(headerName, headerValue);
1✔
307
                }
308
            }
309
            if (requestEntity != null) {
1✔
310
                if (traces.contains(Trace.RequestBody)) {
1✔
311
                    requestBuilder.setBody(binaryBody ? binaryBody(requestEntity) : getContentAsText(requestEntity));
1✔
312
                } else if (traces.contains(Trace.RequestBodySummary)) {
1✔
313
                    requestBuilder.setBody(binaryBody
1✔
314
                            ? binaryBody(requestEntity)
1✔
315
                            : truncateAsNeeded(getContentAsText(requestEntity), TRUNCATE_LENGTH));
1✔
316
                }
317
            }
318
        }
319
        if (response != null) {
1✔
320
            boolean binaryBody = false;
1✔
321
            responseBuilder.withStatus(response.getStatusText());
1✔
322
            if (traces.contains(Trace.ResponseHeaders) && response.getHeaders() != null) {
1✔
323
                responseBuilder.withHeaders();
1✔
324
                for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
1✔
325
                    String headerName = header.getKey();
1✔
326
                    String headerValue = header.getValue();
1✔
327
                    if ("Content-Disposition".equals(headerName)) {
1✔
328
                        binaryBody = true;
×
329
                    }
330
                    responseBuilder.addHeader(headerName, headerValue);
1✔
331
                }
1✔
332
            }
333
            if (responseEntity != null) {
1✔
334
                if (traces.contains(Trace.ResponseBody)) {
1✔
335
                    responseBuilder.setBody(binaryBody ? binaryBody(responseEntity) : getContentAsText(responseEntity));
1✔
336
                } else if (traces.contains(Trace.ResponseBodySummary)) {
1✔
337
                    responseBuilder.setBody(binaryBody
1✔
338
                            ? binaryBody(responseEntity)
×
339
                            : truncateAsNeeded(getContentAsText(responseEntity), TRUNCATE_LENGTH));
1✔
340
                }
341
            }
342
        }
343
        return new RequestAndResponseData(requestBuilder.build(), responseBuilder.build());
1✔
344
    }
345

346
    static String binaryBody(HttpEntitySnapshot entity) {
347
        return "**possibly-binary(type:" + entity.getContentType() + ", len:" + entity.getContentLength() + ")**";
1✔
348
    }
349

350
    /**
351
     * Get the Content as a String
352
     */
353
    public static String getContentAsText(HttpEntitySnapshot entity) {
354
        if (entity == null) {
1✔
355
            return "";
×
356
        }
357
        byte[] contentBytes = entity.getContentArray();
1✔
358
        String contentAsText;
359
        contentAsText = new String(contentBytes, StandardCharsets.UTF_8);
1✔
360
        return contentAsText;
1✔
361
    }
362

363
    /**
364
     * Truncate the string to the desired length if needed
365
     */
366
    public static String truncateAsNeeded(String string, int truncateLen) {
367
        if (truncateLen == -1) {
1✔
368
            return string;
×
369
        }
370
        truncateLen = Math.min(string.length(), truncateLen);
1✔
371
        String suffix = truncateLen < string.length() ? "..." : "";
1✔
372
        return string.substring(0, truncateLen) + suffix;
1✔
373
    }
374
}
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