-
Notifications
You must be signed in to change notification settings - Fork 59
Introducing Golang Code Example documentation to use this Rulio Engine #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| <!-- | ||
|
|
||
| Copyright 2015 Comcast Cable Communications Management, LLC | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
|
|
||
| End Copyright --> | ||
|
|
||
| ## Overview | ||
|
|
||
| This is a sample go client. It has following use cases. | ||
| * Add a Fact | ||
| * Search a Fact | ||
| * List Rules | ||
| * Process an Event | ||
|
|
||
|
|
||
|
|
||
| ## Usage | ||
|
|
||
| ### Starting | ||
| To run execute go run main.go | ||
| .../rulio\examples\go-client>go run main.go | ||
|
|
||
|
|
||
| ### Shell variant | ||
|
|
||
| ```Shell | ||
| # Write a fact. | ||
| curl -s -d 'fact={"city":"London"}' "$ENDPOINT/api/loc/facts/add?location=$LOCATION" | ||
|
|
||
| # Search a fact. | ||
| curl -s -d 'pattern={"have":"?x"}' "$ENDPOINT/api/loc/facts/search?location=$LOCATION" | ||
|
|
||
| # Create a simple rule. | ||
| cat <<EOF | curl -s -d "@-" "$ENDPOINT/api/loc/rules/add?location=$LOCATION" | ||
| {"rule": {"when":{"pattern":{"code":"SEC-3423"},"age":"30"}, | ||
| "condition":{"pattern":{"city":"?x"}}, | ||
| "action":{"code":"var msg = 'city ' + x; console.log(msg); msg;"}}} | ||
| EOF | ||
|
|
||
| # List a rule | ||
| curl -s "$ENDPOINT/loc/rules/list?location=$LOCATION" | ||
|
|
||
| # Process a event. | ||
| curl -d 'event={ "name":"John", "age":30, "city":"London", "code":"SEC-3423" }' | ||
| "$ENDPOINT/api/loc/events/ingest?location=$LOCATION" | python3 -mjson.tool | ||
|
|
||
| ``` | ||
| ### Sample output | ||
| ```Shell | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The text in this block isn't shell input. Maybe just drop the |
||
| *Adding Facts city=London | ||
| Facts input arguments are : map[fact:map[city:London] location:here]. | ||
| Facts is created with id 6a86e47a-e9af-49d1-80f5-c2bd4f0f1fd1. | ||
|
|
||
| *Searching Facts city:?x | ||
| Fact found with value &{[{{"city":"London"} 6a86e47a-e9af-49d1-80f5-c2bd4f0f1fd1 [map[?x:London]]}] 1 0 0} | ||
| Rule created with id: a7b6bf9d-5c0e-4f56-a046-023aab0bb01b | ||
|
|
||
| *Listing Rules available rule | ||
| Available Rules [a7b6bf9d-5c0e-4f56-a046-023aab0bb01b] | ||
|
|
||
| *Processing for incoming events --> { "name":"John", "age":30, "city":"London", "code":"SEC-3423" } | ||
| city London | ||
| {"id":"ID is: ","result":"498a3b4e-5c44-4942-bab3-9380f8361bff Work is: ""}%!(EXTRA *core.FindRules=&{map[age:30 city:London code:SEC-3423 name:John] complete [0xc0002fe280] [city Lond | ||
| on]}) | ||
| Result is: { | ||
| "event": { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This JSON should probably be in Markdown JSON code block: {"like":"this"} |
||
| "age": 30, | ||
| "city": "London", | ||
| "code": "SEC-3423", | ||
| "name": "John" | ||
| }, | ||
| "disposition": { | ||
| "msg": "complete", | ||
| "status": "complete" | ||
| }, | ||
| "children": [ | ||
| { | ||
| "rule": { | ||
| "id": "a7b6bf9d-5c0e-4f56-a046-023aab0bb01b", | ||
| "when": { | ||
| "pattern": { | ||
| "code": "SEC-3423" | ||
| } | ||
| }, | ||
| "condition": { | ||
| "pattern": { | ||
| "city": "?x" | ||
| } | ||
| }, | ||
| "actions": [ | ||
| { | ||
| "code": "var msg = 'city ' + x; console.log(msg); msg;", | ||
| "endpoint": "javascript", | ||
| "subvars": true | ||
| } | ||
| ], | ||
| "once": false, | ||
| "props": null, | ||
| "expires": 0 | ||
| }, | ||
| "bindingss": [ | ||
| { | ||
| "?event": { | ||
| "age": 30, | ||
| "city": "London", | ||
| "code": "SEC-3423", | ||
| "name": "John" | ||
| }, | ||
| "?location": "here", | ||
| "?ruleId": "a7b6bf9d-5c0e-4f56-a046-023aab0bb01b" | ||
| } | ||
| ], | ||
| "disposition": { | ||
| "msg": "complete", | ||
| "status": "complete" | ||
| }, | ||
| "children": [ | ||
| { | ||
| "bindings": { | ||
| "?event": { | ||
| "age": 30, | ||
| "city": "London", | ||
| "code": "SEC-3423", | ||
| "name": "John" | ||
| }, | ||
| "?location": "here", | ||
| "?ruleId": "a7b6bf9d-5c0e-4f56-a046-023aab0bb01b" | ||
| }, | ||
| "disposition": { | ||
| "msg": "complete", | ||
| "status": "complete" | ||
| }, | ||
| "children": [ | ||
| { | ||
| "bindings": { | ||
| "?event": { | ||
| "age": 30, | ||
| "city": "London", | ||
| "code": "SEC-3423", | ||
| "name": "John" | ||
| }, | ||
| "?location": "here", | ||
| "?ruleId": "a7b6bf9d-5c0e-4f56-a046-023aab0bb01b", | ||
| "?x": "London" | ||
| }, | ||
| "action": { | ||
| "code": "var msg = 'city ' + x; console.log(msg); msg;", | ||
| "endpoint": "javascript", | ||
| "subvars": true | ||
| }, | ||
| "disposition": { | ||
| "msg": "complete", | ||
| "status": "complete" | ||
| }, | ||
| "value": "city London" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "DoneWork": { | ||
| "disposition": { | ||
| "msg": "complete", | ||
| "status": "complete" | ||
| } | ||
| } | ||
| } | ||
| ], | ||
| "values": ["city London"] | ||
| } | ||
|
|
||
|
|
||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // +build sample_client | ||
|
|
||
|
|
||
| package configuration | ||
|
|
||
| import "fmt" | ||
| import "github.com/kelseyhightower/envconfig" | ||
|
|
||
| type AppEnvConfigPoc struct { | ||
| EngineEnvConfig | ||
| GenericEnvConfig | ||
| } | ||
|
|
||
| type EngineEnvConfig struct { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like we shouldn't repeat this kind of |
||
| // "linear (or indexed) state? | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please see guidelines for commenting Go code. |
||
| LinearState bool `envconfig:"LinearState" default:"false" ` | ||
| // max pending requests; 0 means no max | ||
| MaxPending int `envconfig:"max-pending" default:"0" required:"true"` | ||
| // Max locations | ||
| MaxLocations int `envconfig:"max-locations" default:"1000" required:"true"` | ||
| // Max facts per location | ||
| MaxFacts int `envconfig:"max-facts" default:"1000" required:"true"` | ||
| // Log accumulator verbosity | ||
| AccVerbosity string `envconfig:"acc-verbosity" default:"EVERYTHING" required:"true"` | ||
| // storage type | ||
| StorageType string `envconfig:"storage" default:"mem" required:"true"` | ||
| // type-specific storage config | ||
| StorageConfig string `envconfig:"storage-config" default:"" required:"false"` | ||
| // port engine will serve | ||
| // enginePort string `envconfig:"engine-port" default:"8001" required:"true"` | ||
| // Location TTL, a duration, 'forever', or 'never' | ||
| LocationTTL string `envconfig:"ttl" default:"forever" required:"true"` | ||
| // Optional URL for external cron service" | ||
| CronURL string `envconfig:"cron-url" default:"" required:"false"` | ||
| // Optional URL for external cron service to reach rules engine | ||
| RulesURL string `envconfig:"rules-url" default:"http://localhost:8001/" required:"true"` | ||
| // Whether to check for state consistency | ||
| CheckState bool `envconfig:"check-state" default:"false" required:"true"` | ||
| // enable Bash script actions | ||
| BashActions bool `envconfig:"bash-actions" default:"false" required:"true"` | ||
| } | ||
|
|
||
| type GenericEnvConfig struct { | ||
| // write cpu profile to this file | ||
| CpuProfile string `envconfig:"cpuprofile" default:"1000" required:"true"` | ||
| // Run an HTTP server that serves profile data; 'none' to turn off | ||
| HttpProfilePort string `envconfig:"httpProfilePort" default:"localhost:6060" required:"true"` | ||
| // Logging verbosity. | ||
| Verbosity string `envconfig:"verbosity" default:"NOTHING" required:"true"` | ||
| // runtime.MemProfileRate | ||
| MemProfileRate int `envconfig:"memProfileRate" default:"524288" required:"true"` | ||
| // runtime.SetBlockProfileRate | ||
| } | ||
|
|
||
| func ParseEnvConfiguration() (*AppEnvConfigPoc, error) { | ||
| conf := &AppEnvConfigPoc{} | ||
| if err := envconfig.Process("", conf); err != nil { | ||
| return nil, fmt.Errorf("Environment variables are not provided: %v ", err) | ||
| } | ||
| return conf, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| // +build sample_client | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/Comcast/rulio/core" | ||
| "github.com/Comcast/rulio/examples/go-client/configuration" | ||
| "github.com/Comcast/rulio/examples/go-client/ruleEngine" | ||
| ) | ||
|
|
||
| func main() { | ||
| fmt.Println("Starting rule engine...") | ||
| envConfig, err := configuration.ParseEnvConfiguration() | ||
| if err != nil { | ||
| fmt.Errorf("Could not parse config: %s", err) | ||
| } | ||
|
|
||
| ctx := core.NewContext("main") | ||
| engine, err := ruleEngine.NewEngine(envConfig, ctx) | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| addFact(engine) | ||
| searchFact(engine) | ||
| createRule(engine) | ||
| listRule(engine) | ||
| processEvent(engine) | ||
| } | ||
|
|
||
| func addFact(engine *ruleEngine.EnginePoc) { | ||
| // Add Facts | ||
| //curl -s -d 'fact={"city":"London"}' "$ENDPOINT/api/loc/facts/add?location=$LOCATION" | ||
| fmt.Print("\nAdding Facts city=London") | ||
| m := make(map[string]interface{}) | ||
| v := make(map[string]interface{}) | ||
|
|
||
| b := []byte(`{"city":"London"}`) | ||
| err := json.Unmarshal(b, &v) | ||
| if err != nil { | ||
| fmt.Printf("\n Unmarshal has failed with error: %v\n", err) | ||
| } | ||
|
|
||
| m["fact"] = v | ||
| m["location"] = "here" | ||
| ctx := engine.Ctx.SubContext() | ||
| err = engine.Service.AddFact(ctx, m) | ||
| if err != nil { | ||
| fmt.Printf("\n Adding Fact failed with error: %v\n", err) | ||
| } | ||
| } | ||
|
|
||
| func searchFact(engine *ruleEngine.EnginePoc) { | ||
| //curl -s -d 'pattern={"have":"?x"}' "$ENDPOINT/api/loc/facts/search?location=$LOCATION" | ||
| fmt.Println("\n\nSearching Facts city:?x ") | ||
| pattern := make(map[string]interface{}) | ||
| v := make(map[string]interface{}) | ||
|
|
||
| b := []byte(`{"city":"?x"}`) | ||
| err := json.Unmarshal(b, &v) | ||
| if err != nil { | ||
| fmt.Printf("\n Unmarshal has failed with error: %v\n", err) | ||
| } | ||
|
|
||
| pattern["pattern"] = v | ||
| pattern["location"] = "here" | ||
| ctx := engine.Ctx.SubContext() | ||
| err = engine.Service.SearchFact(ctx, pattern) | ||
| if err != nil { | ||
| fmt.Printf("\n Searching Fact failed with error: %v\n", err) | ||
| } | ||
| } | ||
|
|
||
| func createRule(engine *ruleEngine.EnginePoc) { | ||
| //{ "name":"John", "age":30, "city":"London", "code":"SEC-3423" } | ||
|
|
||
| /* cat <<EOF | curl -s -d "@-" "$ENDPOINT/api/loc/rules/add?location=$LOCATION" | ||
| {"rule": {"when":{"pattern":{"code":"SEC-3423"},"age":"30"}, | ||
| "condition":{"pattern":{"city":"?x"}}, | ||
| "action":{"code":"var msg = 'city ' + x; console.log(msg); msg;"}}} | ||
| EOF*/ | ||
|
|
||
| rule := make(map[string]interface{}) | ||
| r := make(map[string]interface{}) | ||
| when := []byte(`{"when":{"pattern":{"code":"SEC-3423"},"age":"30"}, | ||
| "condition":{"pattern":{"city":"?x"}}, | ||
| "action":{"code":"var msg = 'city ' + x; console.log(msg); msg;"}}`) | ||
| err := json.Unmarshal(when, &r) | ||
| if err != nil { | ||
| fmt.Printf("\n Unmarshal has failed with error: %v\n", err) | ||
| } | ||
|
|
||
| rule["rule"] = r | ||
| rule["location"] = "here" | ||
| ctx := engine.Ctx.SubContext() | ||
| err = engine.Service.AddRule(ctx, rule) | ||
| if err != nil { | ||
| fmt.Printf("\n Rule creation failed with error: %v\n", err) | ||
| } | ||
| } | ||
|
|
||
| func listRule(engine *ruleEngine.EnginePoc) { | ||
| //curl -s "$ENDPOINT/loc/rules/list?location=$LOCATION" | ||
| fmt.Println("\n\nListing Rules available rule") | ||
| rule := make(map[string]interface{}) | ||
| rule["location"] = "here" | ||
| ctx := engine.Ctx.SubContext() | ||
| err := engine.Service.ListRule(ctx, rule) | ||
| if err != nil { | ||
| fmt.Printf("\n Rule creation failed with error: %v\n", err) | ||
| } | ||
| } | ||
|
|
||
| func processEvent(engine *ruleEngine.EnginePoc) { | ||
| fmt.Println("\n\nProcessing for incoming events --> { \"name\":\"John\", \"age\":30, \"city\":\"London\", \"code\":\"SEC-3423\" } ") | ||
|
|
||
| /*curl -d 'event={ "name":"John", "age":30, "city":"London", "code":"SEC-3423" }' | ||
| "$ENDPOINT/api/loc/events/ingest?location=$LOCATION" | python3 -mjson.tool | ||
| */ | ||
|
|
||
| event := make(map[string]interface{}) | ||
| r := make(map[string]interface{}) | ||
| e := []byte(`{ "name":"John", "age":30, "city":"London", "code":"SEC-3423" }`) | ||
| err := json.Unmarshal(e, &r) | ||
| if err != nil { | ||
| fmt.Printf("\n Unmarshal has failed with error: %v\n", err) | ||
| } | ||
| event["event"] = r | ||
| event["location"] = "here" | ||
| ctx := engine.Ctx.SubContext() | ||
| err = engine.Service.ProcessEvent(ctx, event) | ||
| if err != nil { | ||
| fmt.Printf("\n Event Processing failed with error: %v\n", err) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those backlashes are confusing (and won't work on non-Windows platforms).
Also that line should probably be marked up as code. Similarly for the code in the line above.