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  }