Skip to content

Commit 779304d

Browse files
committed
Allow skipping JSON check and add JSONLines
1 parent f15d744 commit 779304d

File tree

3 files changed

+75
-10
lines changed

3 files changed

+75
-10
lines changed

docs/configuration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,7 @@ Examples:
12971297
| body | any | no | |
12981298
| frameless | boolean | no | false |
12991299
| allow-insecure | boolean | no | false |
1300+
| skip-json-validation | boolean | no | false |
13001301
| template | string | yes | |
13011302
| parameters | key (string) & value (string|array) | no | |
13021303
| subrequests | map of requests | no | |
@@ -1344,6 +1345,9 @@ When set to `true`, removes the border and padding around the widget.
13441345
##### `allow-insecure`
13451346
Whether to ignore invalid/self-signed certificates.
13461347

1348+
##### `skip-json-validation`
1349+
When set to `true`, skips the JSON validation step. This is useful when the API returns JSON Lines/newline-delimited JSON, which is a format that consists of several JSON objects separated by newlines.
1350+
13471351
##### `template`
13481352
The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
13491353

docs/custom-api.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,55 @@ You can also access the response headers:
309309
<div>{{ .Response.Header.Get "Content-Type" }}</div>
310310
```
311311

312+
<hr>
313+
314+
JSON response:
315+
316+
```json
317+
{"name": "Steve", "age": 30}
318+
{"name": "Alex", "age": 25}
319+
{"name": "John", "age": 35}
320+
```
321+
322+
The above format is "[ndjson](https://docs.mulesoft.com/dataweave/latest/dataweave-formats-ndjson)" or "[JSON Lines](https://jsonlines.org/)", where each line is a separate JSON object. To parse this format, you must first disable the JSON validation check in your config, since by default the response is expected to be a single valid JSON object:
323+
324+
```yaml
325+
- type: custom-api
326+
skip-json-validation: true
327+
```
328+
329+
Then, to iterate over each object you can use `.JSONLines`:
330+
331+
```html
332+
{{ range .JSONLines }}
333+
<p>{{ .String "name" }} is {{ .Int "age" }} years old</p>
334+
{{ end }}
335+
```
336+
337+
Output:
338+
339+
```html
340+
<p>Steve is 30 years old</p>
341+
<p>Alex is 25 years old</p>
342+
<p>John is 35 years old</p>
343+
```
344+
345+
For other ways of selecting data from a JSON Lines response, have a look at the docs for [tidwall/gjson](https://github.com/tidwall/gjson/tree/master?tab=readme-ov-file#json-lines). For example, to get an array of all names, you can use the following:
346+
347+
```html
348+
{{ range .JSON.Array "..#.name" }}
349+
<p>{{ .String "" }}</p>
350+
{{ end }}
351+
```
352+
353+
Output:
354+
355+
```html
356+
<p>Steve</p>
357+
<p>Alex</p>
358+
<p>John</p>
359+
```
360+
312361
## Functions
313362

314363
The following functions are available on the `JSON` object:

internal/glance/widget-custom-api.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ var customAPIWidgetTemplate = mustParseTemplate("custom-api.html", "widget-base.
2525

2626
// Needs to be exported for the YAML unmarshaler to work
2727
type CustomAPIRequest struct {
28-
URL string `yaml:"url"`
29-
AllowInsecure bool `yaml:"allow-insecure"`
30-
Headers map[string]string `yaml:"headers"`
31-
Parameters queryParametersField `yaml:"parameters"`
32-
Method string `yaml:"method"`
33-
BodyType string `yaml:"body-type"`
34-
Body any `yaml:"body"`
35-
bodyReader io.ReadSeeker `yaml:"-"`
36-
httpRequest *http.Request `yaml:"-"`
28+
URL string `yaml:"url"`
29+
AllowInsecure bool `yaml:"allow-insecure"`
30+
Headers map[string]string `yaml:"headers"`
31+
Parameters queryParametersField `yaml:"parameters"`
32+
Method string `yaml:"method"`
33+
BodyType string `yaml:"body-type"`
34+
Body any `yaml:"body"`
35+
SkipJSONValidation bool `yaml:"skip-json-validation"`
36+
bodyReader io.ReadSeeker `yaml:"-"`
37+
httpRequest *http.Request `yaml:"-"`
3738
}
3839

3940
type customAPIWidget struct {
@@ -157,6 +158,17 @@ type customAPITemplateData struct {
157158
subrequests map[string]*customAPIResponseData
158159
}
159160

161+
func (data *customAPITemplateData) JSONLines() []decoratedGJSONResult {
162+
result := make([]decoratedGJSONResult, 0, 5)
163+
164+
gjson.ForEachLine(data.JSON.Raw, func(line gjson.Result) bool {
165+
result = append(result, decoratedGJSONResult{line})
166+
return true
167+
})
168+
169+
return result
170+
}
171+
160172
func (data *customAPITemplateData) Subrequest(key string) *customAPIResponseData {
161173
req, exists := data.subrequests[key]
162174
if !exists {
@@ -190,7 +202,7 @@ func fetchCustomAPIRequest(ctx context.Context, req *CustomAPIRequest) (*customA
190202

191203
body := strings.TrimSpace(string(bodyBytes))
192204

193-
if body != "" && !gjson.Valid(body) {
205+
if !req.SkipJSONValidation && body != "" && !gjson.Valid(body) {
194206
truncatedBody, isTruncated := limitStringLength(body, 100)
195207
if isTruncated {
196208
truncatedBody += "... <truncated>"

0 commit comments

Comments
 (0)