github.com/Jeffail/benthos/v3@v3.65.0/public/bloblang/environment.go (about) 1 package bloblang 2 3 import ( 4 "github.com/Jeffail/benthos/v3/internal/bloblang" 5 "github.com/Jeffail/benthos/v3/internal/bloblang/parser" 6 "github.com/Jeffail/benthos/v3/internal/bloblang/query" 7 ) 8 9 // Environment provides an isolated Bloblang environment where the available 10 // features, functions and methods can be modified. 11 type Environment struct { 12 env *bloblang.Environment 13 } 14 15 // GlobalEnvironment returns the global default environment. Modifying this 16 // environment will impact all Bloblang parses that aren't initialized with an 17 // isolated environment, as well as any new environments initialized after the 18 // changes. 19 func GlobalEnvironment() *Environment { 20 return &Environment{ 21 env: bloblang.GlobalEnvironment(), 22 } 23 } 24 25 // NewEnvironment creates a fresh Bloblang environment, starting with the full 26 // range of globally defined features (functions and methods), and provides APIs 27 // for expanding or contracting the features available to this environment. 28 // 29 // It's worth using an environment when you need to restrict the access or 30 // capabilities that certain bloblang mappings have versus others. 31 // 32 // For example, an environment could be created that removes any functions for 33 // accessing environment variables or reading data from the host disk, which 34 // could be used in certain situations without removing those functions globally 35 // for all mappings. 36 func NewEnvironment() *Environment { 37 return GlobalEnvironment().WithoutFunctions().WithoutMethods() 38 } 39 40 // NewEmptyEnvironment creates a fresh Bloblang environment starting completely 41 // empty, where no functions or methods are initially available. 42 func NewEmptyEnvironment() *Environment { 43 return &Environment{ 44 env: bloblang.NewEmptyEnvironment(), 45 } 46 } 47 48 // Parse a Bloblang mapping using the Environment to determine the features 49 // (functions and methods) available to the mapping. 50 // 51 // When a parsing error occurs the error will be the type *ParseError, which 52 // gives access to the line and column where the error occurred, as well as a 53 // method for creating a well formatted error message. 54 func (e *Environment) Parse(blobl string) (*Executor, error) { 55 exec, err := e.env.NewMapping(blobl) 56 if err != nil { 57 if pErr, ok := err.(*parser.Error); ok { 58 return nil, internalToPublicParserError([]rune(blobl), pErr) 59 } 60 return nil, err 61 } 62 return newExecutor(exec), nil 63 } 64 65 // RegisterMethod adds a new Bloblang method to the environment. All method 66 // names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ (snake 67 // case). 68 func (e *Environment) RegisterMethod(name string, ctor MethodConstructor) error { 69 spec := query.NewMethodSpec(name, "").InCategory(query.MethodCategoryPlugin, "") 70 spec.Params = query.VariadicParams() 71 return e.env.RegisterMethod(spec, func(target query.Function, args *query.ParsedParams) (query.Function, error) { 72 fn, err := ctor(args.Raw()...) 73 if err != nil { 74 return nil, err 75 } 76 return query.ClosureFunction("method "+name, func(ctx query.FunctionContext) (interface{}, error) { 77 v, err := target.Exec(ctx) 78 if err != nil { 79 return nil, err 80 } 81 return fn(v) 82 }, target.QueryTargets), nil 83 }) 84 } 85 86 // RegisterMethodV2 adds a new Bloblang method to the environment using a 87 // provided ParamsSpec to define the name of the method and its parameters. 88 // 89 // Plugin names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ 90 // (snake case). 91 func (e *Environment) RegisterMethodV2(name string, spec *PluginSpec, ctor MethodConstructorV2) error { 92 category := query.MethodCategory(spec.category) 93 if category == "" { 94 category = query.MethodCategoryPlugin 95 } 96 var examples []query.ExampleSpec 97 for _, e := range spec.examples { 98 var res []string 99 for _, inputOutput := range e.inputOutputs { 100 res = append(res, inputOutput[0], inputOutput[1]) 101 } 102 examples = append(examples, query.NewExampleSpec(e.summary, e.mapping, res...)) 103 } 104 iSpec := query.NewMethodSpec(name, spec.description).InCategory(category, "", examples...) 105 iSpec.Params = spec.params 106 return e.env.RegisterMethod(iSpec, func(target query.Function, args *query.ParsedParams) (query.Function, error) { 107 fn, err := ctor(&ParsedParams{par: args}) 108 if err != nil { 109 return nil, err 110 } 111 return query.ClosureFunction("method "+name, func(ctx query.FunctionContext) (interface{}, error) { 112 v, err := target.Exec(ctx) 113 if err != nil { 114 return nil, err 115 } 116 return fn(v) 117 }, target.QueryTargets), nil 118 }) 119 } 120 121 // RegisterFunction adds a new Bloblang function to the environment. All 122 // function names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ 123 // (snake case). 124 func (e *Environment) RegisterFunction(name string, ctor FunctionConstructor) error { 125 spec := query.NewFunctionSpec(query.FunctionCategoryPlugin, name, "") 126 spec.Params = query.VariadicParams() 127 return e.env.RegisterFunction(spec, func(args *query.ParsedParams) (query.Function, error) { 128 fn, err := ctor(args.Raw()...) 129 if err != nil { 130 return nil, err 131 } 132 return query.ClosureFunction("function "+name, func(ctx query.FunctionContext) (interface{}, error) { 133 return fn() 134 }, nil), nil 135 }) 136 } 137 138 // RegisterFunctionV2 adds a new Bloblang function to the environment using a 139 // provided ParamsSpec to define the name of the function and its parameters. 140 // 141 // Plugin names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ 142 // (snake case). 143 func (e *Environment) RegisterFunctionV2(name string, spec *PluginSpec, ctor FunctionConstructorV2) error { 144 category := query.FunctionCategory(spec.category) 145 if category == "" { 146 category = query.FunctionCategoryPlugin 147 } 148 var examples []query.ExampleSpec 149 for _, e := range spec.examples { 150 var res []string 151 for _, inputOutput := range e.inputOutputs { 152 res = append(res, inputOutput[0], inputOutput[1]) 153 } 154 examples = append(examples, query.NewExampleSpec(e.summary, e.mapping, res...)) 155 } 156 iSpec := query.NewFunctionSpec(category, name, spec.description, examples...) 157 iSpec.Params = spec.params 158 return e.env.RegisterFunction(iSpec, func(args *query.ParsedParams) (query.Function, error) { 159 fn, err := ctor(&ParsedParams{par: args}) 160 if err != nil { 161 return nil, err 162 } 163 return query.ClosureFunction("function "+name, func(ctx query.FunctionContext) (interface{}, error) { 164 return fn() 165 }, nil), nil 166 }) 167 } 168 169 // WithoutMethods returns a copy of the environment but with a variadic list of 170 // method names removed. Instantiation of these removed methods within a mapping 171 // will cause errors at parse time. 172 func (e *Environment) WithoutMethods(names ...string) *Environment { 173 return &Environment{ 174 env: e.env.WithoutMethods(names...), 175 } 176 } 177 178 // WithoutFunctions returns a copy of the environment but with a variadic list 179 // of function names removed. Instantiation of these removed functions within a 180 // mapping will cause errors at parse time. 181 func (e *Environment) WithoutFunctions(names ...string) *Environment { 182 return &Environment{ 183 env: e.env.WithoutFunctions(names...), 184 } 185 } 186 187 // WithDisabledImports returns a copy of the environment where imports within 188 // mappings are disabled. 189 func (e *Environment) WithDisabledImports() *Environment { 190 return &Environment{ 191 env: e.env.WithDisabledImports(), 192 } 193 } 194 195 // WithCustomImporter returns a copy of the environment where imports from 196 // mappings are done via a provided closure function. 197 func (e *Environment) WithCustomImporter(fn func(name string) ([]byte, error)) *Environment { 198 return &Environment{ 199 env: e.env.WithCustomImporter(fn), 200 } 201 } 202 203 // WithMaxMapRecursion returns a copy of the environment where the maximum 204 // recursion allowed for maps is set to a given value. If the execution of a 205 // mapping from this environment matches this number of recursive map calls the 206 // mapping will error out. 207 func (e *Environment) WithMaxMapRecursion(n int) *Environment { 208 return &Environment{ 209 env: e.env.WithMaxMapRecursion(n), 210 } 211 } 212 213 //------------------------------------------------------------------------------ 214 215 // Parse a Bloblang mapping allowing the use of the globally accessible range of 216 // features (functions and methods). 217 // 218 // When a parsing error occurs the error will be the type *ParseError, which 219 // gives access to the line and column where the error occurred, as well as a 220 // method for creating a well formatted error message. 221 func Parse(blobl string) (*Executor, error) { 222 exec, err := parser.ParseMapping(parser.GlobalContext(), blobl) 223 if err != nil { 224 return nil, internalToPublicParserError([]rune(blobl), err) 225 } 226 return newExecutor(exec), nil 227 } 228 229 // RegisterMethod adds a new Bloblang method to the global environment. All 230 // method names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ 231 // (snake case). 232 func RegisterMethod(name string, ctor MethodConstructor) error { 233 return GlobalEnvironment().RegisterMethod(name, ctor) 234 } 235 236 // RegisterMethodV2 adds a new Bloblang method to the global environment. All 237 // method names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ 238 // (snake case). 239 func RegisterMethodV2(name string, spec *PluginSpec, ctor MethodConstructorV2) error { 240 return GlobalEnvironment().RegisterMethodV2(name, spec, ctor) 241 } 242 243 // RegisterFunction adds a new Bloblang function to the global environment. All 244 // function names must match the regular expression /^[a-z0-9]+(_[a-z0-9]+)*$/ 245 // (snake case). 246 func RegisterFunction(name string, ctor FunctionConstructor) error { 247 return GlobalEnvironment().RegisterFunction(name, ctor) 248 } 249 250 // RegisterFunctionV2 adds a new Bloblang function to the global environment. 251 // All function names must match the regular expression 252 // /^[a-z0-9]+(_[a-z0-9]+)*$/ (snake case). 253 func RegisterFunctionV2(name string, spec *PluginSpec, ctor FunctionConstructorV2) error { 254 return GlobalEnvironment().RegisterFunctionV2(name, spec, ctor) 255 } 256 257 // WalkFunctions executes a provided function argument for every function that 258 // has been registered to the environment. 259 func (e *Environment) WalkFunctions(fn func(name string, spec *FunctionView)) { 260 e.env.WalkFunctions(func(name string, spec query.FunctionSpec) { 261 v := &FunctionView{spec: spec} 262 fn(name, v) 263 }) 264 } 265 266 // WalkMethods executes a provided function argument for every method that has 267 // been registered to the environment. 268 func (e *Environment) WalkMethods(fn func(name string, spec *MethodView)) { 269 e.env.WalkMethods(func(name string, spec query.MethodSpec) { 270 v := &MethodView{spec: spec} 271 fn(name, v) 272 }) 273 }