Skip to content

Commit 291f6b9

Browse files
Abigael-JTeemhu
andauthored
Handle possible nulls from fromJson() in TeragrepDynatraceStep.java (#622)
* checks that JsonObject is not null * rebased * changed exception type * rebased * limited try catch block to relevant objects only * removes instance of check * run spotless * changes accidentally removed during rebase * Update src/main/java/com/teragrep/pth_10/steps/teragrep/TeragrepDynatraceStep.java refactored to remove null checks Co-authored-by: eemhu <[email protected]> * run spotless --------- Co-authored-by: eemhu <[email protected]>
1 parent 64cddf2 commit 291f6b9

3 files changed

Lines changed: 143 additions & 21 deletions

File tree

src/main/java/com/teragrep/pth_10/steps/teragrep/TeragrepDynatraceStep.java

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,15 @@
4545
*/
4646
package com.teragrep.pth_10.steps.teragrep;
4747

48-
import com.google.gson.Gson;
49-
import com.google.gson.JsonElement;
50-
import com.google.gson.JsonNull;
48+
import com.google.gson.*;
5149
import com.google.gson.JsonObject;
5250
import com.teragrep.functions.dpf_02.AbstractStep;
5351
import com.teragrep.pth_10.ast.DPLParserCatalystContext;
5452
import com.teragrep.pth_10.ast.NumericText;
5553
import com.teragrep.pth_10.ast.TextString;
5654
import com.teragrep.pth_10.steps.Flushable;
5755
import com.teragrep.pth_10.steps.teragrep.dynatrace.DynatraceItem;
56+
import org.apache.http.HttpEntity;
5857
import org.apache.http.client.methods.CloseableHttpResponse;
5958
import org.apache.http.client.methods.HttpPost;
6059
import org.apache.http.entity.StringEntity;
@@ -159,35 +158,54 @@ private void sendPostReq(String urlStr, DynatraceItem dti) throws IOException {
159158
httpPost.setEntity(new StringEntity(dti.toString(), "utf-8"));
160159

161160
try (
162-
CloseableHttpClient client = HttpClients.createDefault();
163-
CloseableHttpResponse response = client.execute(httpPost)
161+
final CloseableHttpClient client = HttpClients.createDefault();
162+
final CloseableHttpResponse response = client.execute(httpPost)
164163
) {
165164
final int statusCode = response.getStatusLine().getStatusCode();
166165

167-
try (InputStream respStream = response.getEntity().getContent()) {
168-
JsonObject jsonResp = new Gson()
169-
.fromJson(new InputStreamReader(respStream, StandardCharsets.UTF_8), JsonObject.class);
170-
JsonElement errorElem = jsonResp.get("error");
171-
if (!(errorElem instanceof JsonNull)) {
172-
throw new RuntimeException("Error from server response: " + errorElem.toString());
173-
}
174-
JsonElement validElem = jsonResp.get("linesValid");
175-
if (validElem == null) {
176-
throw new RuntimeException("Unexpected JSON: Could not find linesValid element.");
166+
final HttpEntity entity = response.getEntity();
167+
168+
if (entity == null) {
169+
throw new IllegalStateException("Response entity was null!");
170+
}
171+
172+
final JsonObject jsonResp;
173+
try (final InputStream respStream = entity.getContent()) {
174+
final InputStreamReader inputStreamReader = new InputStreamReader(respStream, StandardCharsets.UTF_8);
175+
try {
176+
jsonResp = new Gson().fromJson(inputStreamReader, JsonObject.class);
177177
}
178-
LOGGER.info("Valid lines: <[{}]>", validElem);
179-
JsonElement invalidElem = jsonResp.get("linesInvalid");
180-
if (invalidElem == null) {
181-
throw new RuntimeException("Unexpected JSON: Could not find linesInvalid element.");
178+
catch (final JsonIOException | JsonSyntaxException je) {
179+
throw new IllegalArgumentException("Error parsing JSON response, message: " + je.getMessage());
182180
}
183-
LOGGER.warn("Invalid lines: <[{}]>", invalidElem);
181+
}
182+
catch (final IOException | UnsupportedOperationException e) {
183+
throw new IllegalStateException("Error getting response stream, message: " + e.getMessage());
184+
}
185+
if (jsonResp == null || jsonResp.isJsonNull()) {
186+
throw new IllegalStateException("Unexpected null JSON response");
184187
}
185188

189+
if (jsonResp.has("error") && !jsonResp.get("error").isJsonNull()) {
190+
throw new RuntimeException("Error from server response: " + jsonResp.get("error").toString());
191+
}
192+
193+
final boolean hasValidElem = jsonResp.has("linesValid");
194+
if (!hasValidElem) {
195+
throw new RuntimeException("Unexpected JSON: Could not find linesValid element.");
196+
}
197+
LOGGER.info("Valid lines: <[{}]>", jsonResp.get("linesValid"));
198+
199+
final boolean hasInvalidElem = jsonResp.has("linesInvalid");
200+
if (!hasInvalidElem) {
201+
throw new RuntimeException("Unexpected JSON: Could not find linesInvalid element.");
202+
}
203+
LOGGER.warn("Invalid lines: <[{}]>", jsonResp.get("linesInvalid"));
204+
186205
if (statusCode != 202 && statusCode != 400) {
187206
throw new RuntimeException("Error! Response code: <[" + statusCode + "]>. Expected 202 or 400.");
188207
}
189208
}
190-
191209
}
192210

193211
@Override
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Teragrep Data Processing Language (DPL) translator for Apache Spark (pth_10)
3+
* Copyright (C) 2019-2025 Suomen Kanuuna Oy
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
*
19+
* Additional permission under GNU Affero General Public License version 3
20+
* section 7
21+
*
22+
* If you modify this Program, or any covered work, by linking or combining it
23+
* with other code, such other code is not for that reason alone subject to any
24+
* of the requirements of the GNU Affero GPL version 3 as long as this Program
25+
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
26+
* modifications.
27+
*
28+
* Supplemented terms under GNU Affero General Public License version 3
29+
* section 7
30+
*
31+
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
32+
* versions must be marked as "Modified version of" The Program.
33+
*
34+
* Names of the licensors and authors may not be used for publicity purposes.
35+
*
36+
* No rights are granted for use of trade names, trademarks, or service marks
37+
* which are in The Program if any.
38+
*
39+
* Licensee must indemnify licensors and authors for any liability that these
40+
* contractual assumptions impose on licensors and authors.
41+
*
42+
* To the extent this program is licensed as part of the Commercial versions of
43+
* Teragrep, the applicable Commercial License may apply to this file if you as
44+
* a licensee so wish it.
45+
*/
46+
package com.teragrep.pth_10;
47+
48+
import org.mockserver.mock.action.ExpectationResponseCallback;
49+
import org.mockserver.model.HttpRequest;
50+
import org.mockserver.model.HttpResponse;
51+
52+
public class DynatraceNullAPICallback implements ExpectationResponseCallback {
53+
54+
public DynatraceNullAPICallback() {
55+
56+
}
57+
58+
@Override
59+
public HttpResponse handle(HttpRequest httpRequest) {
60+
return HttpResponse.response().withStatusCode(202);
61+
}
62+
}

src/test/java/com/teragrep/pth_10/TeragrepDynatraceTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ public void tgDynatraceTest() {
132132

133133
// two lines received
134134
mockServer.verify(request().withPath("/metrics/ingest"), VerificationTimes.exactly(2));
135+
136+
mockServer
137+
.clear(request().withPath("/metrics/ingest").withMethod("POST").withHeader("Content-Type", "text/plain; charset=utf-8"));
138+
135139
}
136140

137141
@Test
@@ -156,6 +160,10 @@ public void tgDynatraceNoAggregateTest() {
156160

157161
// 0 lines received
158162
mockServer.verify(request().withPath("/metrics/ingest"), VerificationTimes.never());
163+
164+
mockServer
165+
.clear(request().withPath("/metrics/ingest").withMethod("POST").withHeader("Content-Type", "text/plain; charset=utf-8"));
166+
159167
}
160168

161169
@Test
@@ -182,5 +190,39 @@ public void tgDynatraceNonNumericDataTest() {
182190

183191
// 0 lines received
184192
mockServer.verify(request().withPath("/metrics/ingest"), VerificationTimes.never());
193+
194+
mockServer
195+
.clear(request().withPath("/metrics/ingest").withMethod("POST").withHeader("Content-Type", "text/plain; charset=utf-8"));
196+
197+
}
198+
199+
@Test
200+
@DisabledIfSystemProperty(
201+
named = "skipSparkTest",
202+
matches = "true"
203+
)
204+
public void tgDynatraceNullResponseTest() {
205+
// respond to metrics ingest
206+
mockServer
207+
.when(request().withPath("/metrics/ingest").withMethod("POST").withHeader("Content-Type", "text/plain; charset=utf-8")).respond(HttpClassCallback.callback(DynatraceNullAPICallback.class));
208+
209+
// send post
210+
Throwable th = this.streamingTestUtil
211+
.performThrowingDPLTest(
212+
IllegalStateException.class, "| makeresults count=10 " + "| eval _raw = 1" + "| stats sum(_raw)"
213+
+ "| teragrep exec dynatrace metric write",
214+
testFile, ds -> {
215+
}
216+
);
217+
218+
// should not work with null response
219+
Assertions.assertEquals("Unexpected null JSON response", th.getMessage());
220+
221+
// 1 lines received before null response
222+
mockServer.verify(request().withPath("/metrics/ingest"), VerificationTimes.once());
223+
224+
mockServer
225+
.clear(request().withPath("/metrics/ingest").withMethod("POST").withHeader("Content-Type", "text/plain; charset=utf-8"));
226+
185227
}
186228
}

0 commit comments

Comments
 (0)