Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions examples/go-client/README.md
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
Copy link
Copy Markdown
Member

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.



### 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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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 Shell?

*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": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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"]
}



```
61 changes: 61 additions & 0 deletions examples/go-client/configuration/EnvConfig.go
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 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we shouldn't repeat this kind of struct here when we have (for example) sys. SystemConfig. Can you refactor to use existing structs?

// "linear (or indexed) state?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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
}
136 changes: 136 additions & 0 deletions examples/go-client/main.go
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)
}
}
Loading