github.com/Jeffail/benthos/v3@v3.65.0/internal/bloblang/environment.go (about) 1 package bloblang 2 3 import ( 4 "github.com/Jeffail/benthos/v3/internal/bloblang/field" 5 "github.com/Jeffail/benthos/v3/internal/bloblang/mapping" 6 "github.com/Jeffail/benthos/v3/internal/bloblang/parser" 7 "github.com/Jeffail/benthos/v3/internal/bloblang/query" 8 ) 9 10 // Environment provides an isolated Bloblang environment where the available 11 // features, functions and methods can be modified. 12 type Environment struct { 13 pCtx parser.Context 14 maxMapRecursion int 15 } 16 17 // GlobalEnvironment returns the global default environment. Modifying this 18 // environment will impact all Bloblang parses that aren't initialized with an 19 // isolated environment, as well as any new environments initialized after the 20 // changes. 21 func GlobalEnvironment() *Environment { 22 return &Environment{ 23 pCtx: parser.GlobalContext(), 24 } 25 } 26 27 // NewEnvironment creates a fresh Bloblang environment, starting with the full 28 // range of globally defined features (functions and methods), and provides APIs 29 // for expanding or contracting the features available to this environment. 30 // 31 // It's worth using an environment when you need to restrict the access or 32 // capabilities that certain bloblang mappings have versus others. 33 // 34 // For example, an environment could be created that removes any functions for 35 // accessing environment variables or reading data from the host disk, which 36 // could be used in certain situations without removing those functions globally 37 // for all mappings. 38 func NewEnvironment() *Environment { 39 return GlobalEnvironment().WithoutFunctions().WithoutMethods() 40 } 41 42 // NewEmptyEnvironment creates a fresh Bloblang environment starting completely 43 // empty, where no functions or methods are initially available. 44 func NewEmptyEnvironment() *Environment { 45 return &Environment{ 46 pCtx: parser.EmptyContext(), 47 } 48 } 49 50 // NewField attempts to parse and create a dynamic field expression from a 51 // string. If the expression is invalid an error is returned. 52 // 53 // When a parsing error occurs the returned error will be a *parser.Error type, 54 // which allows you to gain positional and structured error messages. 55 func (e *Environment) NewField(expr string) (*field.Expression, error) { 56 f, err := parser.ParseField(e.pCtx, expr) 57 if err != nil { 58 return nil, err 59 } 60 return f, nil 61 } 62 63 // NewMapping parses a Bloblang mapping using the Environment to determine the 64 // features (functions and methods) available to the mapping. 65 // 66 // When a parsing error occurs the error will be the type *parser.Error, which 67 // gives access to the line and column where the error occurred, as well as a 68 // method for creating a well formatted error message. 69 func (e *Environment) NewMapping(blobl string) (*mapping.Executor, error) { 70 exec, err := parser.ParseMapping(e.pCtx, blobl) 71 if err != nil { 72 return nil, err 73 } 74 if e.maxMapRecursion > 0 { 75 exec.SetMaxMapRecursion(e.maxMapRecursion) 76 } 77 return exec, nil 78 } 79 80 // Deactivated returns a version of the environment where constructors are 81 // disabled for all functions and methods, allowing mappings to be parsed and 82 // validated but not executed. 83 // 84 // The underlying register of functions and methods is shared with the target 85 // environment, and therefore functions/methods registered to this set will also 86 // be added to the still activated environment. Use the Without methods (with 87 // empty args if applicable) in order to create a deep copy of the environment 88 // that is independent of the source. 89 func (e *Environment) Deactivated() *Environment { 90 env := *e 91 env.pCtx = env.pCtx.Deactivated() 92 return &env 93 } 94 95 // OnlyPure removes any methods and functions that have been registered but are 96 // marked as impure. Impure in this context means the method/function is able to 97 // mutate global state or access machine state (read environment variables, 98 // files, etc). Note that methods/functions that access the machine clock are 99 // not marked as pure, so timestamp functions will still work. 100 func (e *Environment) OnlyPure() *Environment { 101 env := *e 102 env.pCtx.Functions = env.pCtx.Functions.OnlyPure() 103 env.pCtx.Methods = env.pCtx.Methods.OnlyPure() 104 return &env 105 } 106 107 // RegisterMethod adds a new Bloblang method to the environment. 108 func (e *Environment) RegisterMethod(spec query.MethodSpec, ctor query.MethodCtor) error { 109 return e.pCtx.Methods.Add(spec, ctor) 110 } 111 112 // RegisterFunction adds a new Bloblang function to the environment. 113 func (e *Environment) RegisterFunction(spec query.FunctionSpec, ctor query.FunctionCtor) error { 114 return e.pCtx.Functions.Add(spec, ctor) 115 } 116 117 // WithImporter returns a new environment where Bloblang imports are performed 118 // from a new importer. 119 func (e *Environment) WithImporter(importer parser.Importer) *Environment { 120 env := *e 121 env.pCtx = env.pCtx.WithImporter(importer) 122 return &env 123 } 124 125 // WithImporterRelativeToFile returns a new environment where any relative 126 // imports will be made from the directory of the provided file path. The 127 // provided path can itself be relative (to the current importer directory) or 128 // absolute. 129 func (e *Environment) WithImporterRelativeToFile(filePath string) *Environment { 130 env := *e 131 env.pCtx = env.pCtx.WithImporterRelativeToFile(filePath) 132 return &env 133 } 134 135 // WithDisabledImports returns a version of the environment where imports within 136 // mappings are disabled entirely. This prevents mappings from accessing files 137 // from the host disk. 138 func (e *Environment) WithDisabledImports() *Environment { 139 env := *e 140 env.pCtx = env.pCtx.DisabledImports() 141 return &env 142 } 143 144 // WithCustomImporter returns a version of the environment where file imports 145 // are done exclusively through a provided closure function, which takes an 146 // import path (relative or absolute). 147 func (e *Environment) WithCustomImporter(fn func(name string) ([]byte, error)) *Environment { 148 env := *e 149 env.pCtx = env.pCtx.CustomImporter(fn) 150 return &env 151 } 152 153 // WithoutMethods returns a copy of the environment but with a variadic list of 154 // method names removed. Instantiation of these removed methods within a mapping 155 // will cause errors at parse time. 156 func (e *Environment) WithoutMethods(names ...string) *Environment { 157 env := *e 158 env.pCtx.Methods = env.pCtx.Methods.Without(names...) 159 return &env 160 } 161 162 // WithoutFunctions returns a copy of the environment but with a variadic list 163 // of function names removed. Instantiation of these removed functions within a 164 // mapping will cause errors at parse time. 165 func (e *Environment) WithoutFunctions(names ...string) *Environment { 166 env := *e 167 env.pCtx.Functions = env.pCtx.Functions.Without(names...) 168 return &env 169 } 170 171 // WithMaxMapRecursion returns a copy of the environment where the maximum 172 // recursion allowed for maps is set to a given value. If the execution of a 173 // mapping from this environment matches this number of recursive map calls the 174 // mapping will error out. 175 func (e *Environment) WithMaxMapRecursion(n int) *Environment { 176 env := *e 177 env.maxMapRecursion = n 178 return &env 179 } 180 181 // WalkFunctions executes a provided function argument for every function that 182 // has been registered to the environment. 183 func (e *Environment) WalkFunctions(fn func(name string, spec query.FunctionSpec)) { 184 for _, f := range e.pCtx.Functions.Docs() { 185 fn(f.Name, f) 186 } 187 } 188 189 // WalkMethods executes a provided function argument for every method that has 190 // been registered to the environment. 191 func (e *Environment) WalkMethods(fn func(name string, spec query.MethodSpec)) { 192 for _, m := range e.pCtx.Methods.Docs() { 193 fn(m.Name, m) 194 } 195 }