github.com/datachainlab/burrow@v0.25.0/deploy/def/jobs.go (about)

     1  package def
     2  
     3  import (
     4  	"regexp"
     5  
     6  	validation "github.com/go-ozzo/ozzo-validation"
     7  	"github.com/go-ozzo/ozzo-validation/is"
     8  	"github.com/hyperledger/burrow/deploy/def/rule"
     9  	"github.com/hyperledger/burrow/execution/evm/abi"
    10  )
    11  
    12  // ------------------------------------------------------------------------
    13  // Proposal Jobs
    14  // ------------------------------------------------------------------------
    15  
    16  type Proposal struct {
    17  	// (Optional), address of the account that signs the proposal or votes for the proposal
    18  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
    19  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
    20  	// know what you're doing)
    21  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
    22  	// (Required), address of the account used for serialising proposals, the proposals system account
    23  	ProposalAddress string `mapstructure:"proposaladdress" json:"proposaladdress" yaml:"proposaladdress" toml:"proposaladdress"`
    24  	// (Optional), sequence of the ProposalAddress
    25  	ProposalSequence string `mapstructure:"proposalsequence" json:"proposalsequence" yaml:"proposalsequence" toml:"proposalsequence"`
    26  	// (Optional)
    27  	VotingPower string `mapstructure:"votingpower" json:"votingpower" yaml:"votingpower" toml:"votingpower"`
    28  	// (Required) the name of the proposal
    29  	Name string `mapstructure:"name" json:"name" yaml:"name" toml:"name"`
    30  	// (Required) the description of the proposal
    31  	Description string `mapstructure:"description" json:"description" yaml:"description" toml:"description"`
    32  	// (Required) the file path of the sub yaml to run
    33  	Jobs []*Job `mapstructure:"jobs" json:"jobs" yaml:"jobs" toml:"jobs"`
    34  }
    35  
    36  func (job *Proposal) Validate() error {
    37  	return validation.ValidateStruct(job,
    38  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
    39  		validation.Field(&job.VotingPower, rule.Uint64OrPlaceholder),
    40  		validation.Field(&job.Name, validation.Required),
    41  		validation.Field(&job.Description, validation.Required),
    42  		validation.Field(&job.Jobs, validation.Required),
    43  	)
    44  }
    45  
    46  // ------------------------------------------------------------------------
    47  // Meta Jobs
    48  // ------------------------------------------------------------------------
    49  
    50  // Used in the Target of UpdateAccount to determine whether to create a new account, e.g. new() or new(key1,ed25519)
    51  var NewKeyRegex = regexp.MustCompile(`new\((?P<keyName>[[:alnum:]]+)?(,(?P<curveType>[[:alnum:]]+))?\)`)
    52  
    53  func KeyNameCurveType(newKeyMatch []string) (keyName, curveType string) {
    54  	for i, name := range NewKeyRegex.SubexpNames() {
    55  		switch name {
    56  		case "keyName":
    57  			keyName = newKeyMatch[i]
    58  		case "curveType":
    59  			curveType = newKeyMatch[i]
    60  		}
    61  	}
    62  	return
    63  }
    64  
    65  type Meta struct {
    66  	// (Required) the file path of the sub yaml to run
    67  	File     string    `mapstructure:"file" json:"file" yaml:"file" toml:"file"`
    68  	Playbook *Playbook `json:"-" yaml:"-" toml:"-"`
    69  }
    70  
    71  func (job *Meta) Validate() error {
    72  	return validation.ValidateStruct(job,
    73  		validation.Field(&job.File, validation.Required),
    74  	)
    75  }
    76  
    77  // ------------------------------------------------------------------------
    78  // Governance Jobs
    79  // ------------------------------------------------------------------------
    80  
    81  type PermissionString string
    82  
    83  func (ps PermissionString) Validate() error {
    84  	return rule.PermissionOrPlaceholder.Validate(ps)
    85  }
    86  
    87  // UpdateAccount updates an account by overwriting the given values, where values are omitted the existing values
    88  // are preserved. Currently requires Root permission on Source account
    89  type UpdateAccount struct {
    90  	// (Optional, if account job or global account set) address of the account from which to send (the
    91  	// public key for the account must be available to burrow keys)
    92  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
    93  	// (Required) The target account that will be governed - either an address or public key (its type will be determined by it's length)
    94  	// if altering power then either a public key must be provided or the requisite public key associated with the address
    95  	// must be available in an connected keys Signer
    96  	Target string `mapstructure:"target" json:"target" yaml:"target" toml:"target"`
    97  	// (Optional) the Tendermint validator power to set for this account
    98  	Power string `mapstructure:"power" json:"power" yaml:"power" toml:"power"`
    99  	// (Optional) The Burrow native token balance to set for this account
   100  	Native string `mapstructure:"native" json:"native" yaml:"native" toml:"native"`
   101  	// (Optional) the permissions to set for this account
   102  	Permissions []PermissionString `mapstructure:"permissions" json:"permissions" yaml:"permissions" toml:"permissions"`
   103  	// (Optional) the account permission roles to set for this account
   104  	Roles []string `mapstructure:"roles" json:"roles" yaml:"roles" toml:"roles"`
   105  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
   106  	// know what you're doing)
   107  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
   108  }
   109  
   110  func (job *UpdateAccount) Validate() error {
   111  	return validation.ValidateStruct(job,
   112  		validation.Field(&job.Target, validation.Required, rule.Or(rule.Placeholder, is.Hexadecimal,
   113  			validation.Match(NewKeyRegex))),
   114  		validation.Field(&job.Permissions),
   115  		validation.Field(&job.Power, rule.Uint64OrPlaceholder),
   116  		validation.Field(&job.Native, rule.Uint64OrPlaceholder),
   117  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
   118  	)
   119  }
   120  
   121  // ------------------------------------------------------------------------
   122  // Util Jobs
   123  // ------------------------------------------------------------------------
   124  
   125  type Account struct {
   126  	// (Required) address of the account which should be used as the default (if source) is
   127  	// not given for future transactions. Will make sure the burrow keys has the public key
   128  	// for the account. Generally account should be the first job called unless it is used
   129  	// via a flag or environment variables to establish what default to use.
   130  	Address string `mapstructure:"address" json:"address" yaml:"address" toml:"address"`
   131  }
   132  
   133  func (job *Account) Validate() error {
   134  	return validation.ValidateStruct(job,
   135  		validation.Field(&job.Address, validation.Required),
   136  	)
   137  }
   138  
   139  type Set struct {
   140  	// (Required) value which should be saved along with the jobName (which will be the key)
   141  	// this is useful to set variables which can be used throughout the jobs definition file (deploy.yaml).
   142  	// It should be noted that arrays and bools must be defined using strings as such "[1,2,3]"
   143  	// if they are intended to be used further in a assert job.
   144  	Value string `mapstructure:"val" json:"val" yaml:"val" toml:"val"`
   145  }
   146  
   147  func (job *Set) Validate() error {
   148  	return validation.ValidateStruct(job,
   149  		validation.Field(&job.Value, validation.Required),
   150  	)
   151  }
   152  
   153  // ------------------------------------------------------------------------
   154  // Transaction Jobs
   155  // ------------------------------------------------------------------------
   156  
   157  type Send struct {
   158  	// (Optional, if account job or global account set) address of the account from which to send (the
   159  	// public key for the account must be available to burrow keys)
   160  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
   161  	// (Required) address of the account to send the tokens
   162  	Destination string `mapstructure:"destination" json:"destination" yaml:"destination" toml:"destination"`
   163  	// (Required) amount of tokens to send from the `source` to the `destination`
   164  	Amount string `mapstructure:"amount" json:"amount" yaml:"amount" toml:"amount"`
   165  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
   166  	// know what you're doing)
   167  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
   168  }
   169  
   170  func (job *Send) Validate() error {
   171  	return validation.ValidateStruct(job,
   172  		validation.Field(&job.Destination, validation.Required),
   173  		validation.Field(&job.Amount, rule.Uint64OrPlaceholder),
   174  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
   175  	)
   176  }
   177  
   178  type RegisterName struct {
   179  	// (Optional, if account job or global account set) address of the account from which to send (the
   180  	// public key for the account must be available to burrow keys)
   181  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
   182  	// (Required - unless providing data file) name which will be registered
   183  	Name string `mapstructure:"name" json:"name" yaml:"name" toml:"name"`
   184  	// (Optional, if data_file is used; otherwise required) data which will be stored at the `name` key
   185  	Data string `mapstructure:"data" json:"data" yaml:"data" toml:"data"`
   186  	// (Optional) csv file in the form (name,data[,amount]) which can be used to bulk register names
   187  	DataFile string `mapstructure:"data_file" json:"data_file" yaml:"data_file" toml:"data_file"`
   188  	// (Optional) amount of blocks which the name entry will be reserved for the registering user
   189  	Amount string `mapstructure:"amount" json:"amount" yaml:"amount" toml:"amount"`
   190  	// (Optional) validators' fee
   191  	Fee string `mapstructure:"fee" json:"fee" yaml:"fee" toml:"fee"`
   192  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
   193  	// know what you're doing)
   194  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
   195  }
   196  
   197  func (job *RegisterName) Validate() error {
   198  	return validation.ValidateStruct(job,
   199  		validation.Field(&job.Amount, rule.Uint64OrPlaceholder),
   200  		validation.Field(&job.Fee, rule.Uint64OrPlaceholder),
   201  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
   202  	)
   203  }
   204  
   205  type Permission struct {
   206  	// (Optional, if account job or global account set) address of the account from which to send (the
   207  	// public key for the account must be available to burrow keys)
   208  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
   209  	// (Required) actions must be in the set ["set_base", "unset_base", "set_global", "add_role" "rm_role"]
   210  	Action string `mapstructure:"action" json:"action" yaml:"action" toml:"action"`
   211  	// (Required, unless add_role or rm_role action selected) the name of the permission flag which is to
   212  	// be updated
   213  	Permission string `mapstructure:"permission" json:"permission" yaml:"permission" toml:"permission"`
   214  	// (Required) the value of the permission or role which is to be updated
   215  	Value string `mapstructure:"value" json:"value" yaml:"value" toml:"value"`
   216  	// (Required) the target account which is to be updated
   217  	Target string `mapstructure:"target" json:"target" yaml:"target" toml:"target"`
   218  	// (Required, if add_role or rm_role action selected) the role which should be given to the account
   219  	Role string `mapstructure:"role" json:"role" yaml:"role" toml:"role"`
   220  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
   221  	// know what you're doing)
   222  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
   223  }
   224  
   225  func (job *Permission) Validate() error {
   226  	return validation.ValidateStruct(job,
   227  		validation.Field(&job.Value, validation.In("true", "false", "")),
   228  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
   229  	)
   230  }
   231  
   232  // ------------------------------------------------------------------------
   233  // Contracts Jobs
   234  // ------------------------------------------------------------------------
   235  
   236  type PackageDeploy struct {
   237  	// TODO
   238  }
   239  
   240  type Build struct {
   241  	// (Required) the filepath to the contract file. this should be relative to the current path **or**
   242  	// relative to the contracts path established via the --dir.
   243  	// If contract has a "bin" file extension then it will not be sent to the
   244  	// compilers but rather will just be sent to the chain. Note, if you use a "call" job after deploying
   245  	// a binary contract then you will be **required** to utilize an abi field in the call job.
   246  	Contract string `mapstructure:"contract" json:"contract" yaml:"contract" toml:"contract"`
   247  	// (Optional) where to save the result of the compilation
   248  	BinPath string `mapstructure:"binpath" json:"binpath" yaml:"binpath" toml:"binpath"`
   249  	// (Optional) the name of contract to instantiate (it has to be one of the contracts present)
   250  	// in the file defined in Contract above.
   251  	// When none is provided, the system will choose the contract with the same name as that file.
   252  	// use "all" to override and deploy all contracts in order. if "all" is selected the result
   253  	// of the job will default to the address of the contract which was deployed that matches
   254  	// the name of the file (or the last one deployed if there are no matching names; not the "last"
   255  	// one deployed" strategy is non-deterministic and should not be used).
   256  	Instance string `mapstructure:"instance" json:"instance" yaml:"instance" toml:"instance"`
   257  	// (Optional) Path to store an extra copy of the bin file
   258  	Store string `mapstructure:"store" json:"store" yaml:"store" toml:"store"`
   259  }
   260  
   261  func (job *Build) Validate() error {
   262  	return validation.ValidateStruct(job,
   263  		validation.Field(&job.Contract, validation.Required),
   264  	)
   265  }
   266  
   267  type Deploy struct {
   268  	// (Optional, if account job or global account set) address of the account from which to send (the
   269  	// public key for the account must be available to burrow keys)
   270  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
   271  	// (Required) the filepath to the contract file. this should be relative to the current path **or**
   272  	// relative to the contracts path established via the --dir.
   273  	// If contract has a "bin" file extension then it will not be sent to the
   274  	// compilers but rather will just be sent to the chain. Note, if you use a "call" job after deploying
   275  	// a binary contract then you will be **required** to utilize an abi field in the call job.
   276  	Contract string `mapstructure:"contract" json:"contract" yaml:"contract" toml:"contract"`
   277  	// (Optional) the name of contract to instantiate (it has to be one of the contracts present)
   278  	// in the file defined in Contract above.
   279  	// When none is provided, the system will choose the contract with the same name as that file.
   280  	// use "all" to override and deploy all contracts in order. if "all" is selected the result
   281  	// of the job will default to the address of the contract which was deployed that matches
   282  	// the name of the file (or the last one deployed if there are no matching names; not the "last"
   283  	// one deployed" strategy is non-deterministic and should not be used).
   284  	Instance string `mapstructure:"instance" json:"instance" yaml:"instance" toml:"instance"`
   285  	// (Optional) the file path for the linkReferences for contract
   286  	Libraries string `mapstructure:"libraries" json:"libraries" yaml:"libraries" toml:"libraries"`
   287  	// (Optional) TODO: additional arguments to send along with the contract code
   288  	Data interface{} `mapstructure:"data" json:"data" yaml:"data" toml:"data"`
   289  	// (Optional) amount of tokens to send to the contract which will (after deployment) reside in the
   290  	// contract's account
   291  	Amount string `mapstructure:"amount" json:"amount" yaml:"amount" toml:"amount"`
   292  	// (Optional) validators' fee
   293  	Fee string `mapstructure:"fee" json:"fee" yaml:"fee" toml:"fee"`
   294  	// (Optional) amount of gas which should be sent along with the contract deployment transaction
   295  	Gas string `mapstructure:"gas" json:"gas" yaml:"gas" toml:"gas"`
   296  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
   297  	// know what you're doing)
   298  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
   299  	// (Optional) todo
   300  	Variables []*abi.Variable
   301  	// (Optional) Path to store an extra copy of the bin file
   302  	Store string `mapstructure:"store" json:"store" yaml:"store" toml:"store"`
   303  }
   304  
   305  func (job *Deploy) Validate() error {
   306  	return validation.ValidateStruct(job,
   307  		validation.Field(&job.Contract, validation.Required),
   308  		validation.Field(&job.Amount, rule.Uint64OrPlaceholder),
   309  		validation.Field(&job.Fee, rule.Uint64OrPlaceholder),
   310  		validation.Field(&job.Gas, rule.Uint64OrPlaceholder),
   311  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
   312  	)
   313  }
   314  
   315  type Call struct {
   316  	// (Optional, if account job or global account set) address of the account from which to send (the
   317  	// public key for the account must be available to burrow keys)
   318  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
   319  	// (Required) address of the contract which should be called
   320  	Destination string `mapstructure:"destination" json:"destination" yaml:"destination" toml:"destination"`
   321  	// (Required unless testing fallback function) function inside the contract to be called
   322  	Function string `mapstructure:"function" json:"function" yaml:"function" toml:"function"`
   323  	// (Optional) data which should be called. will use the monax-abi tooling under the hood to formalize the
   324  	// transaction
   325  	Data interface{} `mapstructure:"data" json:"data" yaml:"data" toml:"data"`
   326  	// (Optional) amount of tokens to send to the contract
   327  	Amount string `mapstructure:"amount" json:"amount" yaml:"amount" toml:"amount"`
   328  	// (Optional) validators' fee
   329  	Fee string `mapstructure:"fee" json:"fee" yaml:"fee" toml:"fee"`
   330  	// (Optional) amount of gas which should be sent along with the call transaction
   331  	Gas string `mapstructure:"gas" json:"gas" yaml:"gas" toml:"gas"`
   332  	// (Optional, advanced only) sequence to use when burrow keys signs the transaction (do not use unless you
   333  	// know what you're doing)
   334  	Sequence string `mapstructure:"sequence" json:"sequence" yaml:"sequence" toml:"sequence"`
   335  	// (Optional) location of the bin file to use (can be relative path or in bin path)
   336  	// deployed contracts save ABI artifacts in the bin folder as *both* the name of the contract
   337  	// and the address where the contract was deployed to
   338  	Bin string `mapstructure:"bin" json:"bin" yaml:"bin" toml:"bin"`
   339  	// (Optional) by default the call job will "store" the return from the contract as the
   340  	// result of the job. If you would like to store the transaction hash instead of the
   341  	// return from the call job as the result of the call job then select "tx" on the save
   342  	// variable. Anything other than "tx" in this field will use the default.
   343  	Save string `mapstructure:"save" json:"save" yaml:"save" toml:"save"`
   344  	// (Optional) the call job's returned variables
   345  	Variables []*abi.Variable
   346  }
   347  
   348  func (job *Call) Validate() error {
   349  	return validation.ValidateStruct(job,
   350  		validation.Field(&job.Destination, validation.Required),
   351  		validation.Field(&job.Amount, rule.Uint64OrPlaceholder),
   352  		validation.Field(&job.Fee, rule.Uint64OrPlaceholder),
   353  		validation.Field(&job.Gas, rule.Uint64OrPlaceholder),
   354  		validation.Field(&job.Sequence, rule.Uint64OrPlaceholder),
   355  	)
   356  }
   357  
   358  // ------------------------------------------------------------------------
   359  // State Jobs
   360  // ------------------------------------------------------------------------
   361  
   362  type DumpState struct {
   363  	WithValidators bool   `mapstructure:"include-validators" json:"include-validators" yaml:"include-validators" toml:"include-validators"`
   364  	ToIPFS         bool   `mapstructure:"to-ipfs" json:"to-ipfs" yaml:"to-ipfs" toml:"to-ipfs"`
   365  	ToFile         bool   `mapstructure:"to-file" json:"to-file" yaml:"to-file" toml:"to-file"`
   366  	IPFSHost       string `mapstructure:"ipfs-host" json:"ipfs-host" yaml:"ipfs-host" toml:"ipfs-host"`
   367  	FilePath       string `mapstructure:"file" json:"file" yaml:"file" toml:"file"`
   368  }
   369  
   370  func (job *DumpState) Validate() error {
   371  	// TODO: write validation logic
   372  	return nil
   373  }
   374  
   375  type RestoreState struct {
   376  	FromIPFS bool   `mapstructure:"from-ipfs" json:"from-ipfs" yaml:"from-ipfs" toml:"from-ipfs"`
   377  	FromFile bool   `mapstructure:"from-file" json:"from-file" yaml:"from-file" toml:"from-file"`
   378  	IPFSHost string `mapstructure:"ipfs-host" json:"ipfs-host" yaml:"ipfs-host" toml:"ipfs-host"`
   379  	FilePath string `mapstructure:"file" json:"file" yaml:"file" toml:"file"`
   380  }
   381  
   382  func (job *RestoreState) Validate() error {
   383  	// TODO: write validation logic
   384  	return nil
   385  }
   386  
   387  // ------------------------------------------------------------------------
   388  // Testing Jobs
   389  // ------------------------------------------------------------------------
   390  
   391  // aka. Simulated Call.
   392  type QueryContract struct {
   393  	// (Optional, if account job or global account set) address of the account from which to send (the
   394  	// public key for the account must be available to burrow keys)
   395  	Source string `mapstructure:"source" json:"source" yaml:"source" toml:"source"`
   396  	// (Required) address of the contract which should be called
   397  	Destination string `mapstructure:"destination" json:"destination" yaml:"destination" toml:"destination"`
   398  	// (Required) data which should be called. will use the monax-abi tooling under the hood to formalize the
   399  	// transaction. QueryContract will usually be used with "accessor" functions in contracts
   400  	Function string `mapstructure:"function" json:"function" yaml:"function" toml:"function"`
   401  	// (Optional) data to be used in the function arguments. Will use the monax-abi tooling under the hood to formalize the
   402  	// transaction.
   403  	Data interface{} `mapstructure:"data" json:"data" yaml:"data" toml:"data"`
   404  	// (Optional) location of the bin file to use (can be relative path or in abi path)
   405  	// deployed contracts save ABI artifacts in the abi folder as *both* the name of the contract
   406  	// and the address where the contract was deployed to
   407  	Bin string `mapstructure:"bin" json:"bin" yaml:"bin" toml:"bin"`
   408  
   409  	Variables []*abi.Variable
   410  }
   411  
   412  func (job *QueryContract) Validate() error {
   413  	return validation.ValidateStruct(job,
   414  		validation.Field(&job.Destination, validation.Required),
   415  	)
   416  }
   417  
   418  type QueryAccount struct {
   419  	// (Required) address of the account which should be queried
   420  	Account string `mapstructure:"account" json:"account" yaml:"account" toml:"account"`
   421  	// (Required) field which should be queried. If users are trying to query the permissions of the
   422  	// account one can get either the `permissions.base` which will return the base permission of the
   423  	// account, or one can get the `permissions.set` which will return the setBit of the account.
   424  	Field string `mapstructure:"field" json:"field" yaml:"field" toml:"field"`
   425  }
   426  
   427  func (job *QueryAccount) Validate() error {
   428  	return validation.ValidateStruct(job,
   429  		validation.Field(&job.Account, validation.Required),
   430  		validation.Field(&job.Field, validation.Required),
   431  	)
   432  }
   433  
   434  type QueryName struct {
   435  	// (Required) name which should be queried
   436  	Name string `mapstructure:"name" json:"name" yaml:"name" toml:"name"`
   437  	// (Required) field which should be quiried (generally will be "data" to get the registered "name")
   438  	Field string `mapstructure:"field" json:"field" yaml:"field" toml:"field"`
   439  }
   440  
   441  func (job *QueryName) Validate() error {
   442  	return validation.ValidateStruct(job,
   443  		validation.Field(&job.Name, validation.Required),
   444  		validation.Field(&job.Field, validation.Required),
   445  	)
   446  }
   447  
   448  type QueryVals struct {
   449  	// (Required) should be of the set ["bonded_validators" or "unbonding_validators"] and it will
   450  	// return a comma separated listing of the addresses which fall into one of those categories
   451  	Query string `mapstructure:"field" json:"field" yaml:"field" toml:"field"`
   452  }
   453  
   454  func (job *QueryVals) Validate() error {
   455  	return validation.ValidateStruct(job,
   456  		validation.Field(&job.Query, validation.Required),
   457  	)
   458  }
   459  
   460  type Assert struct {
   461  	// (Required) key which should be used for the assertion. This is usually known as the "expected"
   462  	// value in most testing suites
   463  	Key string `mapstructure:"key" json:"key" yaml:"key" toml:"key"`
   464  	// (Required) must be of the set ["eq", "ne", "ge", "gt", "le", "lt", "==", "!=", ">=", ">", "<=", "<"]
   465  	// establishes the relation to be tested by the assertion. If a strings key:value pair is being used
   466  	// only the equals or not-equals relations may be used as the key:value will try to be converted to
   467  	// ints for the remainder of the relations. if strings are passed to them then `monax pkgs do` will return an
   468  	// error
   469  	Relation string `mapstructure:"relation" json:"relation" yaml:"relation" toml:"relation"`
   470  	// (Required) value which should be used for the assertion. This is usually known as the "given"
   471  	// value in most testing suites. Generally it will be a variable expansion from one of the query
   472  	// jobs.
   473  	Value string `mapstructure:"val" json:"val" yaml:"val" toml:"val"`
   474  }
   475  
   476  func (job *Assert) Validate() error {
   477  	return validation.ValidateStruct(job,
   478  		validation.Field(&job.Relation, validation.Required, rule.Relation),
   479  	)
   480  }