Skip to content

Commit 256db4e

Browse files
authored
feat: enhance HTTP extractor's script options (#470)
- Allow configuration of the max_allocs and max_const_objects that limit memory allocation in the script env. - Pass the recipe's scope as a script global so that it can be used to construct the asset's URN if needed.
1 parent 7ce6a55 commit 256db4e

File tree

4 files changed

+48
-16
lines changed

4 files changed

+48
-16
lines changed

plugins/extractors/http/README.md

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@ source:
5757
5858
## Inputs
5959
60-
| Key | Value | Example | Description | Required? |
61-
|:----------------|:---------|:---------------------------------------|:------------------------------------------------------------------------------------------------|:----------|
62-
| `request` | `Object` | see [Request](#request) | The configuration for constructing and sending HTTP request. | ✅ |
63-
| `success_codes` | `[]int` | `[200]` | The list of status codes that would be considered as a successful response. Default is `[200]`. | ✘ |
64-
| `concurrency` | `int` | `5` | Number of concurrent child requests to execute. Default is `5` | ✘ |
65-
| `script.engine` | `string` | `tengo` | Script engine. Only `"tengo"` is supported currently | ✅ |
66-
| `script.source` | `string` | see [Worked Example](#worked-example). | [Tengo][tengo] script used to map the response into 0 or more assets. | ✅ |
60+
| Key | Value | Example | Description | Required? |
61+
|:---------------------------|:---------|:---------------------------------------|:------------------------------------------------------------------------------------------------|:----------|
62+
| `request` | `Object` | see [Request](#request) | The configuration for constructing and sending HTTP request. | ✅ |
63+
| `success_codes` | `[]int` | `[200]` | The list of status codes that would be considered as a successful response. Default is `[200]`. | ✘ |
64+
| `concurrency` | `int` | `5` | Number of concurrent child requests to execute. Default is `5` | ✘ |
65+
| `script.engine` | `string` | `tengo` | Script engine. Only `"tengo"` is supported currently | ✅ |
66+
| `script.source` | `string` | see [Worked Example](#worked-example). | [Tengo][tengo] script used to map the response into 0 or more assets. | ✅ |
67+
| `script.max_allocs` | `int` | 10000 | The max number of object allocations allowed during the script run time. Default is `5000`. | ✘ |
68+
| `script.max_const_objects` | `int` | 1000 | The maximum number of constant objects in the compiled script. Default is `500`. | ✘ |
6769

6870
### Request
6971

@@ -92,6 +94,29 @@ source:
9294

9395
### Script Globals
9496

97+
- [`recipe_scope`](#recipe_scope)
98+
- [`response`](#response)
99+
- [`new_asset(string): Asset`](#new_assetstring-asset)
100+
- [`emit(Asset)`](#emitasset)
101+
- [`execute_request(...requests): []Response`](#executerequestrequests-response)
102+
- [`exit`](#exit)
103+
104+
#### `recipe_scope`
105+
106+
The value of the scope specified in the recipe (string).
107+
108+
With the following example recipe:
109+
110+
```yaml
111+
source:
112+
scope: integration
113+
type: http
114+
config:
115+
#...
116+
```
117+
118+
The value of `recipe_scope` will be `integration`.
119+
95120
#### `response`
96121

97122
HTTP response received with the `status_code`, `header` and `body`. Ex:
@@ -150,7 +175,7 @@ asset.data.full_name = "Daiyamondo Jozu"
150175
Takes an asset and emits the asset that can then be consumed by the
151176
processor/sink.
152177

153-
#### `execute_request(...requests)`
178+
#### `execute_request(...requests): []Response`
154179

155180
Takes 1 or more requests and executes the requests with the concurrency defined
156181
in the recipe. The results are returned as an array. Each item in the array can
@@ -315,8 +340,8 @@ source:
315340
}
316341
```
317342

318-
This would emit a 'User' asset for each user object in `response.data`. Note
319-
that the response headers can be accessed under `response.header` and can be
343+
This would emit a 'User' asset for each user object in `response.data`. Note
344+
that the response headers can be accessed under `response.header` and can be
320345
used as needed.
321346

322347
## Caveats

plugins/extractors/http/execute_script.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ import (
2020
)
2121

2222
func (e *Extractor) executeScript(ctx context.Context, res interface{}, emit plugins.Emit) error {
23+
scriptCfg := e.config.Script
2324
s, err := tengoutil.NewSecureScript(
24-
([]byte)(e.config.Script.Source), e.scriptGlobals(ctx, res, emit),
25+
([]byte)(scriptCfg.Source), e.scriptGlobals(ctx, res, emit),
2526
)
2627
if err != nil {
2728
return err
2829
}
2930

31+
s.SetMaxAllocs(scriptCfg.MaxAllocs)
32+
s.SetMaxConstObjects(scriptCfg.MaxConstObjects)
33+
3034
c, err := s.Compile()
3135
if err != nil {
3236
return fmt.Errorf("compile: %w", err)
@@ -41,7 +45,8 @@ func (e *Extractor) executeScript(ctx context.Context, res interface{}, emit plu
4145

4246
func (e *Extractor) scriptGlobals(ctx context.Context, res interface{}, emit plugins.Emit) map[string]interface{} {
4347
return map[string]interface{}{
44-
"response": res,
48+
"recipe_scope": &tengo.String{Value: e.UrnScope},
49+
"response": res,
4550
"new_asset": &tengo.UserFunction{
4651
Name: "new_asset",
4752
Value: newAssetWrapper(),

plugins/extractors/http/http_extractor.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ type Config struct {
3434
SuccessCodes []int `mapstructure:"success_codes" validate:"dive,gte=200,lt=300" default:"[200]"`
3535
Concurrency int `mapstructure:"concurrency" validate:"gte=1,lte=100" default:"5"`
3636
Script struct {
37-
Engine string `mapstructure:"engine" validate:"required,oneof=tengo"`
38-
Source string `mapstructure:"source" validate:"required"`
37+
Engine string `mapstructure:"engine" validate:"required,oneof=tengo"`
38+
Source string `mapstructure:"source" validate:"required"`
39+
MaxAllocs int64 `mapstructure:"max_allocs" validate:"gt=100" default:"5000"`
40+
MaxConstObjects int `mapstructure:"max_const_objects" validate:"gt=10" default:"500"`
3941
} `mapstructure:"script"`
4042
}
4143

plugins/extractors/http/http_extractor_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ func TestExtract(t *testing.T) {
293293
body := response.body
294294
asset := new_asset("user")
295295
// URN format: "urn:{service}:{scope}:{type}:{id}"
296-
asset.urn = format("urn:%s:staging:user:%s", "my_usr_svc", body.employee_id)
296+
asset.urn = format("urn:%s:%s:user:%s", "my_usr_svc", recipe_scope, body.employee_id)
297297
asset.name = body.fullname
298298
asset.service = "my_usr_svc"
299299
// asset.type = "user" // not required, new_asset("user") sets the field.
@@ -322,7 +322,7 @@ func TestExtract(t *testing.T) {
322322
)
323323
},
324324
expected: []*v1beta2.Asset{{
325-
Urn: "urn:my_usr_svc:staging:user:395f8292-d48b-431b-9e2d-63b3dcd4b986",
325+
Urn: "urn:my_usr_svc:test-http:user:395f8292-d48b-431b-9e2d-63b3dcd4b986",
326326
Name: "Van Stump",
327327
Service: "my_usr_svc",
328328
Type: "user",

0 commit comments

Comments
 (0)