Skip to content
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

Feature activity #4

Merged
merged 30 commits into from
Nov 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
57b123c
Added circuit breaker activity
pointlander Oct 26, 2018
5f099c7
Added flogo install command to readme
pointlander Oct 27, 2018
2d245a7
Added anomaly activity
pointlander Oct 27, 2018
aff4d71
Removed flogo install command
pointlander Oct 27, 2018
48cbec8
Added sql detector activity
pointlander Oct 28, 2018
b8a26f2
Fixed sqld flogo json
pointlander Oct 28, 2018
2520db4
Use assert for testing
pointlander Oct 28, 2018
4659ac4
Use flogo contrib master
pointlander Oct 28, 2018
a174ecc
Added rate limiter activity
pointlander Oct 29, 2018
5575623
Expressions which produce errors should be considered false
pointlander Oct 29, 2018
adeb161
Support new logger interface
pointlander Oct 29, 2018
a6a34ca
Added js activity
pointlander Oct 29, 2018
6ce2091
initial jwt service commit
AkshayGadikar Oct 29, 2018
52d717f
added api
AkshayGadikar Oct 30, 2018
a7b4ee2
added api
AkshayGadikar Oct 30, 2018
352a235
changed claims type
AkshayGadikar Oct 30, 2018
3a70606
Added activity docs
pointlander Oct 30, 2018
477aa14
Improved README
pointlander Oct 30, 2018
4279ace
Fixed typo
pointlander Oct 30, 2018
e75bda9
Fixed heading
pointlander Oct 30, 2018
03af82a
added flogo.json with changes
AkshayGadikar Oct 30, 2018
d70d19c
code clean
AkshayGadikar Oct 30, 2018
51671cc
Merge pull request #3 from project-flogo/feature-jwt
pointlander Oct 30, 2018
87fe4e5
Added JWT README
pointlander Oct 30, 2018
93ac23e
Fixed pattern
pointlander Oct 31, 2018
63aae43
Added default channel pattern test
pointlander Oct 31, 2018
13c8b7d
Updated go.mod
pointlander Oct 31, 2018
df3c9a0
Wait for server to start up
pointlander Oct 31, 2018
49cad1e
Removed js activity
pointlander Nov 1, 2018
13df6d6
Update go.mod
pointlander Nov 1, 2018
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
55 changes: 50 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
# Microgateway Action
This is a microgateway action which supports the conditional evaluation of activities. The microgateway has one setting: 'uri' which is the URL of the microgateway JSON resource.
This is a microgateway action which supports the conditional evaluation of activities. The microgateway has one setting: 'uri' which is the URI of the microgateway JSON resource.

## Resource Schema
# Resource

The JSON Schema for the Microgateway resource can be found [here](https://github.com/project-flogo/microgateway/tree/master/internal/schema/schema.json).
## Schema

The JSON Schema for the Microgateway resource can be found [here](internal/schema/schema.json).

## Sections

### Services

A service defines a function or activity of some sort that will be utilized in a step within an execution flow. Services have names, refs, and settings. Any Flogo activity works as a service. Services that are specific to a microgateway can be found [here](activity). Services may call external endpoints like HTTP servers or may stay within the context of the gateway, like the [js](activity/js) activity. Once a service is defined it can be used as many times as needed within your routes and steps.

### Steps

Each microgateway is composed of a number of steps. Each step is evaluated in the order in which it is defined via an optional `if` condition. If the condition is `true`, that step is executed. If that condition is `false` the execution context moves onto the next step in the process and evaluates that one. A blank or omitted `if` condition always evaluates to `true`.

A simple step looks like:

```json
{
"if": "$.payload.pathParams.petId == 9",
"service": "PetStorePets",
"input": {
"method": "GET",
"pathParams.id": "=$payload.pathParams.petId"
}
}
```

As you can see above, a step consists of a simple condition, a service reference, input parameters, and (not shown) output parameters. The `service` must map to a service defined in the `services` array that is defined in the microgateway resource. Input key and value pairs are translated and handed off to the service execution. Output key value pairs are translated and retained after the service has executed. Values starting with `=` are evaluated as variables within the context of the execution. An optional `halt` condition is supported for steps. When the `halt` condition is true the execution of the steps is halted.

### Responses

Each microgateway has an optional set of responses that can be evaluated and returned to the invoking trigger. Much like routes, the first response with an `if` condition evaluating to true is the response that gets executed and returned. A response contains an `if` condition, an `error` boolean, a `code` value, and a `data` object. The `error` boolean dictates whether or not an error should be returned to the engine. The `code` is the status code returned to the trigger. The `data` object is evaluated within the context of the execution and then sent back to the trigger as well.

A simple response looks like:

```json
{
"if": "$.PetStorePets.outputs.result.status == 'available'",
"error": false,
"code": 200,
"data": {
"body.pet": "=$.PetStorePets.outputs.result",
"body.inventory": "=$.PetStoreInventory.outputs.result"
}
}
```

## Example Flogo JSON Usage of a Microgateway Action

An example of a basic gateway can be found [here](https://github.com/project-flogo/microgateway/tree/master/examples/json/basic-gateway).
An example of a basic gateway can be found [here](examples/json/basic-gateway).

## Example Flogo API Usage of a Microgateway Action

An API example can be found [here](https://github.com/project-flogo/microgateway/tree/master/examples/api/basic-gateway).
An API example can be found [here](examples/api/basic-gateway).
73 changes: 55 additions & 18 deletions action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,28 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"

_ "github.com/project-flogo/contrib/function"
"github.com/project-flogo/core/action"
"github.com/project-flogo/core/activity"
"github.com/project-flogo/core/app/resource"
"github.com/project-flogo/core/data"
"github.com/project-flogo/core/data/expression"
_ "github.com/project-flogo/core/data/expression/script"
"github.com/project-flogo/core/data/mapper"
"github.com/project-flogo/core/data/metadata"
"github.com/project-flogo/core/data/resolve"
"github.com/project-flogo/core/support/logger"
logger "github.com/project-flogo/core/support/log"
"github.com/project-flogo/microgateway/api"
"github.com/project-flogo/microgateway/internal/core"
_ "github.com/project-flogo/microgateway/internal/function"
"github.com/project-flogo/microgateway/internal/pattern"
"github.com/project-flogo/microgateway/internal/schema"
)

var log = logger.GetLogger("microgateway")
var log = logger.ChildLogger(logger.RootLogger(), "microgateway")

// Action is the microgateway action
type Action struct {
Expand Down Expand Up @@ -68,6 +71,7 @@ type Factory struct {

type initContext struct {
settings map[string]interface{}
name string
}

func (i *initContext) Settings() map[string]interface{} {
Expand All @@ -78,6 +82,10 @@ func (i *initContext) MapperFactory() mapper.Factory {
return nil
}

func (i *initContext) Logger() logger.Logger {
return logger.ChildLogger(log, i.name)
}

func (f *Factory) Initialize(ctx action.InitContext) error {
f.Manager = ctx.ResourceManager()
return nil
Expand Down Expand Up @@ -126,22 +134,63 @@ func (f *Factory) New(config *action.Config) (action.Action, error) {
return nil, errors.New("no definition found for microgateway")
}

envFlags := make(map[string]string)
for _, e := range os.Environ() {
pair := strings.Split(e, "=")
envFlags[pair[0]] = pair[1]
}
executionContext := map[string]interface{}{
"async": act.settings.Async,
"env": envFlags,
"conf": config.Settings,
}
scope := data.NewSimpleScope(executionContext, nil)

expressionFactory := expression.NewFactory(resolve.GetBasicResolver())
getExpression := func(value interface{}) (*core.Expr, error) {
if stringValue, ok := value.(string); ok && len(stringValue) > 0 && stringValue[0] == '=' {
expr, err := expressionFactory.NewExpr(stringValue[1:])
if err != nil {
return nil, err
}
return core.NewExpr(stringValue, expr), nil
}
return core.NewExpr(fmt.Sprintf("%v", value), expression.NewLiteralExpr(value)), nil
}

services := make(map[string]*core.Service, len(actionData.Services))
for i := range actionData.Services {
name := actionData.Services[i].Name
if _, ok := services[name]; ok {
return nil, fmt.Errorf("duplicate service name: %s", name)
}

values := make(map[string]*core.Expr, len(actionData.Services[i].Settings))
for key, value := range actionData.Services[i].Settings {
values[key], err = getExpression(value)
if err != nil {
log.Infof("expression parsing error: %s", value)
return nil, err
}
}

settings, err := core.TranslateMappings(scope, values)
if err != nil {
return nil, err
}

if ref := actionData.Services[i].Ref; ref != "" {
if factory := activity.GetFactory(ref); factory != nil {
actvt, err := factory(&initContext{settings: actionData.Services[i].Settings})
actvt, err := factory(&initContext{
settings: settings,
name: name,
})
if err != nil {
return nil, err
}
services[name] = &core.Service{
Name: name,
Settings: actionData.Services[i].Settings,
Settings: settings,
Activity: actvt,
}
continue
Expand All @@ -152,32 +201,20 @@ func (f *Factory) New(config *action.Config) (action.Action, error) {
}
services[name] = &core.Service{
Name: name,
Settings: actionData.Services[i].Settings,
Settings: settings,
Activity: actvt,
}
} else if handler := actionData.Services[i].Handler; handler != nil {
services[name] = &core.Service{
Name: name,
Settings: actionData.Services[i].Settings,
Settings: settings,
Activity: &core.Adapter{Handler: handler},
}
} else {
return nil, fmt.Errorf("no ref or handler for service: %s", name)
}
}

expressionFactory := expression.NewFactory(resolve.GetBasicResolver())
getExpression := func(value interface{}) (*core.Expr, error) {
if stringValue, ok := value.(string); ok && len(stringValue) > 0 && stringValue[0] == '=' {
expr, err := expressionFactory.NewExpr(stringValue[1:])
if err != nil {
return nil, err
}
return core.NewExpr(stringValue, expr), nil
}
return core.NewExpr(fmt.Sprintf("%v", value), expression.NewLiteralExpr(value)), nil
}

steps, responses := actionData.Steps, actionData.Responses
microgateway := core.Microgateway{
Name: actionData.Name,
Expand Down
99 changes: 99 additions & 0 deletions action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package microgateway
import (
"context"
"fmt"
"net/http"
"testing"
"time"

"github.com/project-flogo/contrib/activity/rest"
coreactivity "github.com/project-flogo/core/activity"
Expand All @@ -12,6 +14,12 @@ import (
"github.com/project-flogo/microgateway/internal/testing/activity"
"github.com/project-flogo/microgateway/internal/testing/trigger"
"github.com/stretchr/testify/assert"

_ "github.com/project-flogo/contrib/activity/channel"
_ "github.com/project-flogo/contrib/activity/rest"
_ "github.com/project-flogo/microgateway/activity/circuitbreaker"
_ "github.com/project-flogo/microgateway/activity/jwt"
_ "github.com/project-flogo/microgateway/activity/ratelimiter"
)

func TestMicrogateway(t *testing.T) {
Expand Down Expand Up @@ -201,6 +209,97 @@ func TestMicrogatewayHandler(t *testing.T) {
assert.False(t, defaultActionHit)
}

type handler struct {
hit bool
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.hit = true
r.Body.Close()
}

func TestMicrogatewayHttpPattern(t *testing.T) {
defer func() {
trigger.Reset()
activity.Reset()
}()

testHandler := handler{}
s := &http.Server{
Addr: ":1234",
Handler: &testHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go func() {
s.ListenAndServe()
}()
_, err := http.Get("http://localhost:1234/")
for err != nil {
_, err = http.Get("http://localhost:1234/")
}
defer s.Shutdown(context.Background())

app := api.NewApp()

trg := app.NewTrigger(&trigger.Trigger{}, &trigger.Settings{ASetting: 1337})
handler, err := trg.NewHandler(&trigger.HandlerSettings{})
assert.Nil(t, err)

action, err := handler.NewAction(&Action{}, map[string]interface{}{
"pattern": "DefaultHttpPattern",
"useRateLimiter": false,
"useJWT": false,
"useCircuitBreaker": false,
"backendUrl": "http://localhost:1234/",
"rateLimit": "3-M",
})
assert.Nil(t, err)
assert.NotNil(t, action)

e, err := api.NewEngine(app)
assert.Nil(t, err)
e.Start()
defer e.Stop()

result, err := trigger.Fire(0, map[string]interface{}{})
assert.Nil(t, err)
assert.NotNil(t, result)

assert.True(t, testHandler.hit)
}

func TestMicrogatewayChannelPattern(t *testing.T) {
defer func() {
trigger.Reset()
activity.Reset()
}()

app := api.NewApp()

trg := app.NewTrigger(&trigger.Trigger{}, &trigger.Settings{ASetting: 1337})
handler, err := trg.NewHandler(&trigger.HandlerSettings{})
assert.Nil(t, err)

action, err := handler.NewAction(&Action{}, map[string]interface{}{
"pattern": "DefaultChannelPattern",
"useJWT": false,
"useCircuitBreaker": false,
})
assert.Nil(t, err)
assert.NotNil(t, action)

e, err := api.NewEngine(app)
assert.Nil(t, err)
e.Start()
defer e.Stop()

result, err := trigger.Fire(0, map[string]interface{}{})
assert.Nil(t, err)
assert.NotNil(t, result)
}

func BenchmarkMicrogateway(b *testing.B) {
defer func() {
trigger.Reset()
Expand Down
7 changes: 7 additions & 0 deletions activity/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
Activities that are very specific to the operation of the Microgateway.

* [anomaly](anomaly) is an anomaly detection engine
* [circuitbreaker](circuitbreaker) is a circuit breaker for protecting failed services
* [js](js) supports the execution of Javascript snippets
* [jwt](jwt) allows for JSON web token based authentication
* [ratelimiter](ratelimiter) is a rate limiter implementation
* [sqld](sqld) is a SQL injection attack detector
Loading