func_provider.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package interpreter
  11. import (
  12. "fmt"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "devt.de/krotik/common/errorutil"
  17. "devt.de/krotik/common/timeutil"
  18. "devt.de/krotik/ecal/engine"
  19. "devt.de/krotik/ecal/parser"
  20. "devt.de/krotik/ecal/scope"
  21. "devt.de/krotik/ecal/stdlib"
  22. "devt.de/krotik/ecal/util"
  23. )
  24. /*
  25. InbuildFuncMap contains the mapping of inbuild functions.
  26. */
  27. var InbuildFuncMap = map[string]util.ECALFunction{
  28. "range": &rangeFunc{&inbuildBaseFunc{}},
  29. "new": &newFunc{&inbuildBaseFunc{}},
  30. "len": &lenFunc{&inbuildBaseFunc{}},
  31. "del": &delFunc{&inbuildBaseFunc{}},
  32. "add": &addFunc{&inbuildBaseFunc{}},
  33. "concat": &concatFunc{&inbuildBaseFunc{}},
  34. "dumpenv": &dumpenvFunc{&inbuildBaseFunc{}},
  35. "doc": &docFunc{&inbuildBaseFunc{}},
  36. "raise": &raise{&inbuildBaseFunc{}},
  37. "addEvent": &addevent{&inbuildBaseFunc{}},
  38. "addEventAndWait": &addeventandwait{&addevent{&inbuildBaseFunc{}}},
  39. "setCronTrigger": &setCronTrigger{&inbuildBaseFunc{}},
  40. }
  41. /*
  42. inbuildBaseFunc is the base structure for inbuild functions providing some
  43. utility functions.
  44. */
  45. type inbuildBaseFunc struct {
  46. }
  47. /*
  48. AssertNumParam converts a general interface{} parameter into a number.
  49. */
  50. func (ibf *inbuildBaseFunc) AssertNumParam(index int, val interface{}) (float64, error) {
  51. var err error
  52. resNum, ok := val.(float64)
  53. if !ok {
  54. resNum, err = strconv.ParseFloat(fmt.Sprint(val), 64)
  55. if err != nil {
  56. err = fmt.Errorf("Parameter %v should be a number", index)
  57. }
  58. }
  59. return resNum, err
  60. }
  61. /*
  62. AssertMapParam converts a general interface{} parameter into a map.
  63. */
  64. func (ibf *inbuildBaseFunc) AssertMapParam(index int, val interface{}) (map[interface{}]interface{}, error) {
  65. valMap, ok := val.(map[interface{}]interface{})
  66. if ok {
  67. return valMap, nil
  68. }
  69. return nil, fmt.Errorf("Parameter %v should be a map", index)
  70. }
  71. /*
  72. AssertListParam converts a general interface{} parameter into a list.
  73. */
  74. func (ibf *inbuildBaseFunc) AssertListParam(index int, val interface{}) ([]interface{}, error) {
  75. valList, ok := val.([]interface{})
  76. if ok {
  77. return valList, nil
  78. }
  79. return nil, fmt.Errorf("Parameter %v should be a list", index)
  80. }
  81. // Range
  82. // =====
  83. /*
  84. rangeFunc is an interator function which returns a range of numbers.
  85. */
  86. type rangeFunc struct {
  87. *inbuildBaseFunc
  88. }
  89. /*
  90. Run executes this function.
  91. */
  92. func (rf *rangeFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  93. var currVal, to float64
  94. var err error
  95. lenargs := len(args)
  96. from := 0.
  97. step := 1.
  98. if lenargs == 0 {
  99. err = fmt.Errorf("Need at least an end range as first parameter")
  100. }
  101. if err == nil {
  102. if stepVal, ok := is[instanceID+"step"]; ok {
  103. step = stepVal.(float64)
  104. from = is[instanceID+"from"].(float64)
  105. to = is[instanceID+"to"].(float64)
  106. currVal = is[instanceID+"currVal"].(float64)
  107. is[instanceID+"currVal"] = currVal + step
  108. // Check for end of iteration
  109. if (from < to && currVal > to) || (from > to && currVal < to) || from == to {
  110. err = util.ErrEndOfIteration
  111. }
  112. } else {
  113. if lenargs == 1 {
  114. to, err = rf.AssertNumParam(1, args[0])
  115. } else {
  116. from, err = rf.AssertNumParam(1, args[0])
  117. if err == nil {
  118. to, err = rf.AssertNumParam(2, args[1])
  119. }
  120. if err == nil && lenargs > 2 {
  121. step, err = rf.AssertNumParam(3, args[2])
  122. }
  123. }
  124. if err == nil {
  125. is[instanceID+"from"] = from
  126. is[instanceID+"to"] = to
  127. is[instanceID+"step"] = step
  128. is[instanceID+"currVal"] = from
  129. currVal = from
  130. }
  131. }
  132. }
  133. if err == nil {
  134. err = util.ErrIsIterator // Identify as iterator
  135. }
  136. return currVal, err
  137. }
  138. /*
  139. DocString returns a descriptive string.
  140. */
  141. func (rf *rangeFunc) DocString() (string, error) {
  142. return "Range function which can be used to iterate over number ranges. Parameters are start, end and step.", nil
  143. }
  144. // New
  145. // ===
  146. /*
  147. newFunc instantiates a new object.
  148. */
  149. type newFunc struct {
  150. *inbuildBaseFunc
  151. }
  152. /*
  153. Run executes this function.
  154. */
  155. func (rf *newFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  156. var res interface{}
  157. err := fmt.Errorf("Need a map as first parameter")
  158. if len(args) > 0 {
  159. var argMap map[interface{}]interface{}
  160. if argMap, err = rf.AssertMapParam(1, args[0]); err == nil {
  161. obj := make(map[interface{}]interface{})
  162. res = obj
  163. _, err = rf.addSuperClasses(vs, is, obj, argMap)
  164. if initObj, ok := obj["init"]; ok {
  165. if initFunc, ok := initObj.(*function); ok {
  166. initvs := scope.NewScope(fmt.Sprintf("newfunc: %v", instanceID))
  167. initis := make(map[string]interface{})
  168. _, err = initFunc.Run(instanceID, initvs, initis, args[1:])
  169. }
  170. }
  171. }
  172. }
  173. return res, err
  174. }
  175. /*
  176. addSuperClasses adds super class functions to a given object.
  177. */
  178. func (rf *newFunc) addSuperClasses(vs parser.Scope, is map[string]interface{},
  179. obj map[interface{}]interface{}, template map[interface{}]interface{}) (interface{}, error) {
  180. var err error
  181. var initFunc interface{}
  182. var initSuperList []interface{}
  183. // First loop into the base classes (i.e. top-most classes)
  184. if super, ok := template["super"]; ok {
  185. if superList, ok := super.([]interface{}); ok {
  186. for _, superObj := range superList {
  187. var superInit interface{}
  188. if superTemplate, ok := superObj.(map[interface{}]interface{}); ok {
  189. superInit, err = rf.addSuperClasses(vs, is, obj, superTemplate)
  190. initSuperList = append(initSuperList, superInit) // Build up the list of super functions
  191. }
  192. }
  193. } else {
  194. err = fmt.Errorf("Property _super must be a list of super classes")
  195. }
  196. }
  197. // Copy all properties from template to obj
  198. for k, v := range template {
  199. // Save previous init function
  200. if funcVal, ok := v.(*function); ok {
  201. newFunction := &function{funcVal.name, nil, obj, funcVal.declaration, funcVal.declarationVS}
  202. if k == "init" {
  203. newFunction.super = initSuperList
  204. initFunc = newFunction
  205. }
  206. obj[k] = newFunction
  207. } else {
  208. obj[k] = v
  209. }
  210. }
  211. return initFunc, err
  212. }
  213. /*
  214. DocString returns a descriptive string.
  215. */
  216. func (rf *newFunc) DocString() (string, error) {
  217. return "New creates a new object instance.", nil
  218. }
  219. // Len
  220. // ===
  221. /*
  222. lenFunc returns the size of a list or map.
  223. */
  224. type lenFunc struct {
  225. *inbuildBaseFunc
  226. }
  227. /*
  228. Run executes this function.
  229. */
  230. func (rf *lenFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  231. var res float64
  232. err := fmt.Errorf("Need a list or a map as first parameter")
  233. if len(args) > 0 {
  234. argList, ok1 := args[0].([]interface{})
  235. argMap, ok2 := args[0].(map[interface{}]interface{})
  236. if ok1 {
  237. res = float64(len(argList))
  238. err = nil
  239. } else if ok2 {
  240. res = float64(len(argMap))
  241. err = nil
  242. }
  243. }
  244. return res, err
  245. }
  246. /*
  247. DocString returns a descriptive string.
  248. */
  249. func (rf *lenFunc) DocString() (string, error) {
  250. return "Len returns the size of a list or map.", nil
  251. }
  252. // Del
  253. // ===
  254. /*
  255. delFunc removes an element from a list or map.
  256. */
  257. type delFunc struct {
  258. *inbuildBaseFunc
  259. }
  260. /*
  261. Run executes this function.
  262. */
  263. func (rf *delFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  264. var res interface{}
  265. err := fmt.Errorf("Need a list or a map as first parameter and an index or key as second parameter")
  266. if len(args) == 2 {
  267. if argList, ok1 := args[0].([]interface{}); ok1 {
  268. var index float64
  269. index, err = rf.AssertNumParam(2, args[1])
  270. if err == nil {
  271. res = append(argList[:int(index)], argList[int(index+1):]...)
  272. }
  273. }
  274. if argMap, ok2 := args[0].(map[interface{}]interface{}); ok2 {
  275. key := fmt.Sprint(args[1])
  276. delete(argMap, key)
  277. res = argMap
  278. err = nil
  279. }
  280. }
  281. return res, err
  282. }
  283. /*
  284. DocString returns a descriptive string.
  285. */
  286. func (rf *delFunc) DocString() (string, error) {
  287. return "Del removes an item from a list or map.", nil
  288. }
  289. // Add
  290. // ===
  291. /*
  292. addFunc adds an element to a list.
  293. */
  294. type addFunc struct {
  295. *inbuildBaseFunc
  296. }
  297. /*
  298. Run executes this function.
  299. */
  300. func (rf *addFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  301. var res interface{}
  302. err := fmt.Errorf("Need a list as first parameter and a value as second parameter")
  303. if len(args) > 1 {
  304. var argList []interface{}
  305. if argList, err = rf.AssertListParam(1, args[0]); err == nil {
  306. if len(args) == 3 {
  307. var index float64
  308. if index, err = rf.AssertNumParam(3, args[2]); err == nil {
  309. argList = append(argList, 0)
  310. copy(argList[int(index+1):], argList[int(index):])
  311. argList[int(index)] = args[1]
  312. res = argList
  313. }
  314. } else {
  315. res = append(argList, args[1])
  316. }
  317. }
  318. }
  319. return res, err
  320. }
  321. /*
  322. DocString returns a descriptive string.
  323. */
  324. func (rf *addFunc) DocString() (string, error) {
  325. return "Add adds an item to a list. The item is added at the optionally given index or at the end if no index is specified.", nil
  326. }
  327. // Concat
  328. // ======
  329. /*
  330. concatFunc joins one or more lists together.
  331. */
  332. type concatFunc struct {
  333. *inbuildBaseFunc
  334. }
  335. /*
  336. Run executes this function.
  337. */
  338. func (rf *concatFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  339. var res interface{}
  340. err := fmt.Errorf("Need at least two lists as parameters")
  341. if len(args) > 1 {
  342. var argList []interface{}
  343. resList := make([]interface{}, 0)
  344. err = nil
  345. for _, a := range args {
  346. if err == nil {
  347. if argList, err = rf.AssertListParam(1, a); err == nil {
  348. resList = append(resList, argList...)
  349. }
  350. }
  351. }
  352. if err == nil {
  353. res = resList
  354. }
  355. }
  356. return res, err
  357. }
  358. /*
  359. DocString returns a descriptive string.
  360. */
  361. func (rf *concatFunc) DocString() (string, error) {
  362. return "Concat joins one or more lists together. The result is a new list.", nil
  363. }
  364. // dumpenv
  365. // =======
  366. /*
  367. dumpenvFunc returns the current variable environment as a string.
  368. */
  369. type dumpenvFunc struct {
  370. *inbuildBaseFunc
  371. }
  372. /*
  373. Run executes this function.
  374. */
  375. func (rf *dumpenvFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  376. return vs.String(), nil
  377. }
  378. /*
  379. DocString returns a descriptive string.
  380. */
  381. func (rf *dumpenvFunc) DocString() (string, error) {
  382. return "Dumpenv returns the current variable environment as a string.", nil
  383. }
  384. // doc
  385. // ===
  386. /*
  387. docFunc returns the docstring of a function.
  388. */
  389. type docFunc struct {
  390. *inbuildBaseFunc
  391. }
  392. /*
  393. Run executes this function.
  394. */
  395. func (rf *docFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  396. var res interface{}
  397. err := fmt.Errorf("Need a function as parameter")
  398. if len(args) > 0 {
  399. funcObj, ok := args[0].(util.ECALFunction)
  400. if args[0] == nil {
  401. // Try to lookup by the given identifier
  402. c := is["astnode"].(*parser.ASTNode).Children[0].Children[0]
  403. astring := c.Token.Val
  404. if len(c.Children) > 0 {
  405. astring = fmt.Sprintf("%v.%v", astring, c.Children[0].Token.Val)
  406. }
  407. // Check for stdlib function
  408. if funcObj, ok = stdlib.GetStdlibFunc(astring); !ok {
  409. // Check for inbuild function
  410. funcObj, ok = InbuildFuncMap[astring]
  411. }
  412. }
  413. if ok {
  414. res, err = funcObj.DocString()
  415. }
  416. }
  417. return res, err
  418. }
  419. /*
  420. DocString returns a descriptive string.
  421. */
  422. func (rf *docFunc) DocString() (string, error) {
  423. return "Doc returns the docstring of a function.", nil
  424. }
  425. // raise
  426. // =====
  427. /*
  428. raise returns an error. Outside of sinks this will stop the code execution
  429. if the error is not handled by try / except. Inside a sink only the specific sink
  430. will fail. This error can be used to break trigger sequences of sinks if
  431. FailOnFirstErrorInTriggerSequence is set.
  432. */
  433. type raise struct {
  434. *inbuildBaseFunc
  435. }
  436. /*
  437. Run executes this function.
  438. */
  439. func (rf *raise) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  440. var err error
  441. var detailMsg string
  442. var detail interface{}
  443. if len(args) > 0 {
  444. err = fmt.Errorf("%v", args[0])
  445. if len(args) > 1 {
  446. if args[1] != nil {
  447. detailMsg = fmt.Sprint(args[1])
  448. }
  449. if len(args) > 2 {
  450. detail = args[2]
  451. }
  452. }
  453. }
  454. erp := is["erp"].(*ECALRuntimeProvider)
  455. node := is["astnode"].(*parser.ASTNode)
  456. return nil, &util.RuntimeErrorWithDetail{
  457. RuntimeError: erp.NewRuntimeError(err, detailMsg, node).(*util.RuntimeError),
  458. Environment: vs,
  459. Data: detail,
  460. }
  461. }
  462. /*
  463. DocString returns a descriptive string.
  464. */
  465. func (rf *raise) DocString() (string, error) {
  466. return "Raise returns an error object.", nil
  467. }
  468. // addEvent
  469. // ========
  470. /*
  471. addevent adds an event to trigger sinks. This function will return immediately
  472. and not wait for the event cascade to finish. Use this function for event cascades.
  473. */
  474. type addevent struct {
  475. *inbuildBaseFunc
  476. }
  477. /*
  478. Run executes this function.
  479. */
  480. func (rf *addevent) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  481. return rf.addEvent(func(proc engine.Processor, event *engine.Event, scope *engine.RuleScope) (interface{}, error) {
  482. var monitor engine.Monitor
  483. parentMonitor, ok := is["monitor"]
  484. if scope != nil || !ok {
  485. monitor = proc.NewRootMonitor(nil, scope)
  486. } else {
  487. monitor = parentMonitor.(engine.Monitor).NewChildMonitor(0)
  488. }
  489. _, err := proc.AddEvent(event, monitor)
  490. return nil, err
  491. }, is, args)
  492. }
  493. func (rf *addevent) addEvent(addFunc func(engine.Processor, *engine.Event, *engine.RuleScope) (interface{}, error),
  494. is map[string]interface{}, args []interface{}) (interface{}, error) {
  495. var res interface{}
  496. var stateMap map[interface{}]interface{}
  497. erp := is["erp"].(*ECALRuntimeProvider)
  498. proc := erp.Processor
  499. if proc.Stopped() {
  500. proc.Start()
  501. }
  502. err := fmt.Errorf("Need at least three parameters: name, kind and state")
  503. if len(args) > 2 {
  504. if stateMap, err = rf.AssertMapParam(3, args[2]); err == nil {
  505. var scope *engine.RuleScope
  506. event := engine.NewEvent(
  507. fmt.Sprint(args[0]),
  508. strings.Split(fmt.Sprint(args[1]), "."),
  509. stateMap,
  510. )
  511. if len(args) > 3 {
  512. var scopeMap map[interface{}]interface{}
  513. // Add optional scope - if not specified it is { "": true }
  514. if scopeMap, err = rf.AssertMapParam(4, args[3]); err == nil {
  515. var scopeData = map[string]bool{}
  516. for k, v := range scopeMap {
  517. b, _ := strconv.ParseBool(fmt.Sprint(v))
  518. scopeData[fmt.Sprint(k)] = b
  519. }
  520. scope = engine.NewRuleScope(scopeData)
  521. }
  522. }
  523. if err == nil {
  524. res, err = addFunc(proc, event, scope)
  525. }
  526. }
  527. }
  528. return res, err
  529. }
  530. /*
  531. DocString returns a descriptive string.
  532. */
  533. func (rf *addevent) DocString() (string, error) {
  534. return "AddEvent adds an event to trigger sinks. This function will return " +
  535. "immediately and not wait for the event cascade to finish.", nil
  536. }
  537. // addEventAndWait
  538. // ===============
  539. /*
  540. addeventandwait adds an event to trigger sinks. This function will return once
  541. the event cascade has finished and return all errors.
  542. */
  543. type addeventandwait struct {
  544. *addevent
  545. }
  546. /*
  547. Run executes this function.
  548. */
  549. func (rf *addeventandwait) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  550. return rf.addEvent(func(proc engine.Processor, event *engine.Event, scope *engine.RuleScope) (interface{}, error) {
  551. var res []interface{}
  552. rm := proc.NewRootMonitor(nil, scope)
  553. m, err := proc.AddEventAndWait(event, rm)
  554. if m != nil {
  555. allErrors := m.(*engine.RootMonitor).AllErrors()
  556. for _, e := range allErrors {
  557. errors := map[interface{}]interface{}{}
  558. for k, v := range e.ErrorMap {
  559. se := v.(*util.RuntimeErrorWithDetail)
  560. // Note: The variable scope of the sink (se.environment)
  561. // was also captured - for now it is not exposed to the
  562. // language environment
  563. errors[k] = map[interface{}]interface{}{
  564. "error": se.Error(),
  565. "type": se.Type.Error(),
  566. "detail": se.Detail,
  567. "data": se.Data,
  568. }
  569. }
  570. item := map[interface{}]interface{}{
  571. "event": map[interface{}]interface{}{
  572. "name": e.Event.Name(),
  573. "kind": strings.Join(e.Event.Kind(), "."),
  574. "state": e.Event.State(),
  575. },
  576. "errors": errors,
  577. }
  578. res = append(res, item)
  579. }
  580. }
  581. return res, err
  582. }, is, args)
  583. }
  584. /*
  585. DocString returns a descriptive string.
  586. */
  587. func (rf *addeventandwait) DocString() (string, error) {
  588. return "AddEventAndWait adds an event to trigger sinks. This function will " +
  589. "return once the event cascade has finished.", nil
  590. }
  591. // setCronTrigger
  592. // ==============
  593. /*
  594. setCronTrigger adds a periodic cron job which fires events.
  595. */
  596. type setCronTrigger struct {
  597. *inbuildBaseFunc
  598. }
  599. /*
  600. Run executes this function.
  601. */
  602. func (ct *setCronTrigger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
  603. var res interface{}
  604. err := fmt.Errorf("Need a cronspec, an event name and an event scope as parameters")
  605. if len(args) > 2 {
  606. var cs *timeutil.CronSpec
  607. cronspec := fmt.Sprint(args[0])
  608. eventname := fmt.Sprint(args[1])
  609. eventkind := strings.Split(fmt.Sprint(args[2]), ".")
  610. erp := is["erp"].(*ECALRuntimeProvider)
  611. proc := erp.Processor
  612. if proc.Stopped() {
  613. proc.Start()
  614. }
  615. if cs, err = timeutil.NewCronSpec(cronspec); err == nil {
  616. res = cs.String()
  617. tick := 0
  618. erp.Cron.RegisterSpec(cs, func() {
  619. tick += 1
  620. now := erp.Cron.NowFunc()
  621. event := engine.NewEvent(eventname, eventkind, map[interface{}]interface{}{
  622. "time": now,
  623. "timestamp": fmt.Sprintf("%d", now.UnixNano()/int64(time.Millisecond)),
  624. "tick": tick,
  625. })
  626. monitor := proc.NewRootMonitor(nil, nil)
  627. _, err := proc.AddEvent(event, monitor)
  628. errorutil.AssertTrue(err == nil,
  629. fmt.Sprintf("Could not add cron event for trigger %v %v %v: %v",
  630. cronspec, eventname, eventkind, err))
  631. })
  632. }
  633. }
  634. return res, err
  635. }
  636. /*
  637. DocString returns a descriptive string.
  638. */
  639. func (ct *setCronTrigger) DocString() (string, error) {
  640. return "setCronTrigger adds a periodic cron job which fires events.", nil
  641. }