Skip to content
This repository has been archived by the owner on Oct 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #62 from bancodobrasil/develop
Browse files Browse the repository at this point in the history
0.6.0
  • Loading branch information
ralphg6 authored Jun 28, 2022
2 parents f69f5ed + ee7e1a4 commit 05cb00f
Show file tree
Hide file tree
Showing 27 changed files with 1,581 additions and 118 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
FEATWS_RULLER_PORT=8000
FEATWS_RULLER_RESOURCE_LOADER_URL=https://myrepo.com/{knowledgeBase}/{version}/rules.grl
FEATWS_RULLER_RESOURCE_LOADER_HEADERS=PRIVATE-TOKEN:123123d12d12d1
TELEMETRY_EXPORTER_URL=http://jaeger:YOURPORT
58 changes: 58 additions & 0 deletions README-PTBR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/abu-lang/goabu.svg)](https://pkg.go.dev/github.com/bancodobrasil/featws-ruller)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/bancodobrasil/featws-ruller/blob/develop/LICENSE)

# Featws Ruller
- O projeto featws-ruller é uma implementação do [grule-rule-engine](https://github.com/hyperjumptech/grule-rule-engine), usado para aveliar planilhas de regaras (.grl).

## Software Necessário
- Será necessário ter instalado em sua máquina a **Linguagem de Programação Go** para rodar o projeto. Você pode fazer o download na pádina oficial [aqui](https://go.dev/doc/install).
- Clone o repositório **featws-transpiler** para a sua máquina local e tenha verifique se o projeto transpiler e ruller estão na mesma pasta.Você pode achar o repositorio do featws-transpiler [aqui](https://github.com/bancodobrasil/featws-transpiler).

## Inicializando o Projeto
- Clone o projeto para sua máquina local.
- Com a pasta do projeto aberto (*../featws-ruller/main.go*), abra o arquivo _main.go_ e o terminal integrado, digite o comando `go run main.go`. Se voce utiliza o sistema OS ou windows, voce tambem pode dar build e executar o projeto com os comandos: `go build && ./featws-ruler.exe`, caso use windows, ou `go build -o ruller && ./ruller $@` se utiliza Mac.

## Testando diferentes folhas de regras
- Verifique se voce possui em seu workspace o **featws-transpiler** e copie o caminho do arquivo .grl para o novo caso a ser testado. Você pode encontrar isso nos casos _tests_ -> cases.
- Agora basta substituir a variável env "FEATWS_RULLER_DEFAULT_RULES" no arquivo .env na regra, pelo novo caminho, e executar conforme as instruções acima.

## Folha de regras de teste com resolvers
- Para testar se o resolver está carregado, você deve definir a URL **featws-resolver-bridge** no arquivo .env.

## Carregando uma folha de regras de uma fonte remota
- Para carregar uma planilha de uma fonte remota, basta alterar a variável .env "FEATWS_RULLER_RESOURCE_LOADER_URL" apontada para sua URL.

# Usando principais endpoints
_Por padrão a porta utilizada será a :8000_
- GET **http://localhost:SUAPORTAESCOLHIDA/**
- Retornará uma mensagem simples ao cliente, como: "FeatWS Ruller Works!!!"

- POST **http://localhost:SUAPORTAESCOLHIDA/api/v1/eval**
- Neste ponto final você deve ter que passar um corpo, que são os parâmetros definidos pela pasta rulesheet no featws-transpiler. Usando o case 0001, por exemplo, o corpo deve ser:
```json
{
"mynumber": "45"
}
```
- Após a solicitação ter sido enviada, a resposta deve ser algo assim: (essa resposta é definida pelo arquivo .featws na pasta ruleshet, nesse caso é false porque a condição é meunúmero> 12)
```json
{
"myboolfeat": false
}
```
- GET **http://localhost:SUAPORTAESCOLHIDA/swagger/index.html**
- No seu navegador, você pode ver a documentação do swagger da api.

- GET **http://localhost:SUAPORTAESCOLHIDA/health/live?full=1**
- Este endpoint verificará o status ativo do aplicação:

- GET **http://localhost:SUAPORTAESCOLHIDA/health/ready?full=1**
- Este endpoint verificará o status se o serviços está pronto para ser usado ​​pelo projeto da ruller.

```json
{
"goroutine-threshold": "OK",
"resolver-bridge": "OK",
"resource-loader": "OK"
}
```
68 changes: 66 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# featws-ruller
[![Go Reference](https://pkg.go.dev/badge/github.com/abu-lang/goabu.svg)](https://pkg.go.dev/github.com/bancodobrasil/featws-ruller)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/bancodobrasil/featws-ruller/blob/develop/LICENSE)
# Featws Ruller
- The featws-ruller project is an implmentation of the [grule-rule-engine](https://github.com/hyperjumptech/grule-rule-engine), used to evalute rulesheets(.grl)

## Required Software
- You must have the **Go Programming Language** installed in your machine to run this project. You can get the official download [here](https://go.dev/doc/install).
Expand All @@ -7,7 +10,68 @@

## Initializate the project
- Clone this project to your local machine.
- On _main.go_ folder (*../featws-ruller/main.go*), open your local terminal and type the command `go run main.go`.
- On _main.go_ folder (*../featws-ruller/main.go*), open your local terminal and type the command `go run main.go`, if your OS is windows, you can by build and run the executable `go build && ./featws-ruler.exe`, or if your OS is mac, type the command `go build -o ruller && ./ruller $@`.

## Testing diferents rulesheets
- Check if you have in your workspace the **featws-transpiler** and copy the path from .grl file for the new case, you can find that on the cases _tests_ -> cases
- Now just replace the env variable "FEATWS_RULLER_DEFAULT_RULES" on .env file on ruller, with the new path, and run like the instructions above.

## Testing rulesheet with resolvers
- To test if the resolver are loaded, you have to set the **featws-resolver-bridge** URL, on the .env file to.

## Load a rulesheet from remote source
- To load a rulesheet from a remote soure, just change the .env variable "FEATWS_RULLER_RESOURCE_LOADER_URL" pointed to your URL.


# Using main endpoints
_By default the port will be :8000_
- GET **http://localhost:YOURSETTEDPORT/**
- Will return simple message to client such as: "FeatWS Ruller Works!!!"

- POST **http://localhost:YOURSEETEDPORT/api/v1/eval**
- On this end point you must have to pass a body, witch is the parameters setted by rulesheet folder on the featws-transpiler. Using case 0001 for example the body should be:
```json
{
"mynumber": "45"
}
```
- Sending the request, response should be something like that: (this response difined by .featws file on ruleshet folder, in that case is false because the condition is mynumber > 12)
```json
{
"myboolfeat": false
}
```
- GET **http://localhost:YOURSEETEDPORT/swagger/index.html**
- On your browser, you can see the swagger documentation of the api.

- GET **http://localhost:YOURSEETEDPORT/health/live?full=1**
- This endpoint will check the live status of the application, just like that:
```json
{
"goroutine-threshold": "OK"
}
```

- GET **http://localhost:YOURSEETEDPORT/health/ready?full=1**
- This endpoint will check the ready status of the services used by ruller project
```json
{
"goroutine-threshold": "OK",
"resolver-bridge": "OK",
"resource-loader": "OK"
}
```













Expand Down
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)

Expand Down Expand Up @@ -50,6 +51,7 @@ func LoadConfig() (err error) {
if err != nil {
if err2, ok := err.(*os.PathError); !ok {
err = err2
log.Errorf("Error on Load Config: %v", err)
return
}
}
Expand Down
89 changes: 89 additions & 0 deletions controllers/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package controllers

import (
"fmt"
"net/http"
"net/url"
"time"

log "github.com/sirupsen/logrus"

"github.com/bancodobrasil/featws-ruller/config"
"github.com/bancodobrasil/healthcheck"
"github.com/bancodobrasil/healthcheck/checks/goroutine"
"github.com/gin-gonic/gin"
"github.com/gsdenys/healthcheck/checks"
)

// HealthController the health endpoints controller
type HealthController struct {
health healthcheck.Handler
}

// NewHealthController ...
func NewHealthController() *HealthController {
return &HealthController{
health: newHandler(),
}
}

var health = healthcheck.NewHandler()

func newHandler() healthcheck.Handler {
cfg := config.GetConfig()
health.AddLivenessCheck("goroutine-threshold", goroutine.Count(100))

if cfg.ResourceLoaderURL != "" {
rawResourceLoaderURL := cfg.ResourceLoaderURL
resourceLoader, _ := url.Parse(rawResourceLoaderURL)

if resourceLoader.Scheme == "" {
log.Fatal("ResourceLoaderURL must have a scheme: http:// or https://")
}

if resourceLoader.Host == "" {
log.Fatal("ResourceLoaderURL must have a host: example.com")
}
finalResourceLoader := resourceLoader.Scheme + "://" + resourceLoader.Host
health.AddReadinessCheck("resource-loader", Get(finalResourceLoader, 1*time.Second))
}

if cfg.ResolverBridgeURL != "" {
resolverBridgeURL := cfg.ResolverBridgeURL
health.AddReadinessCheck("resolver-bridge", Get(resolverBridgeURL, 1*time.Second))
}

return health
}

// Get ...
func Get(url string, timeout time.Duration) checks.Check {
client := http.Client{
Timeout: timeout,
}

return func() error {
resp, err := client.Get(url)

if err != nil {
return err
}

resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("returned status %d", resp.StatusCode)
}

return nil
}
}

// HealthLiveHandler ...
func (c *HealthController) HealthLiveHandler() gin.HandlerFunc {
return gin.WrapH(http.HandlerFunc(c.health.LiveEndpoint))
}

// HealthReadyHandler ...
func (c *HealthController) HealthReadyHandler() gin.HandlerFunc {
return gin.WrapH(http.HandlerFunc(c.health.ReadyEndpoint))
}
2 changes: 1 addition & 1 deletion controllers/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
//HomeHandler ...
func HomeHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(http.StatusOK, "FeatWS Works!!!")
c.String(http.StatusOK, "FeatWS Ruller Works!!!")
}

}
2 changes: 1 addition & 1 deletion controllers/home_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestHomeHandler(t *testing.T) {
}

gotBody := r.Body.String()
expectedBody := "FeatWS Works!!!"
expectedBody := "FeatWS Ruller Works!!!"

if gotBody != expectedBody {
t.Error("got error on request homeHandler func")
Expand Down
55 changes: 34 additions & 21 deletions controllers/v1/evalHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,63 @@ import (
"net/http"
"sync"

payloads "github.com/bancodobrasil/featws-ruller/payloads/v1"
"github.com/bancodobrasil/featws-ruller/services"
"github.com/bancodobrasil/featws-ruller/types"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)

// DefaultKnowledgeBaseName its default name of Knowledge Base
const DefaultKnowledgeBaseName = "default"

// DefaultKnowledgeBaseVersion its default version of Knowledge Base
const DefaultKnowledgeBaseVersion = "latest"

//LoadMutex ...
var loadMutex sync.Mutex

//EvalHandler ...
// EvalHandler godoc
// @Summary Evaluate the rulesheet
// @Description Receive the params to execute the rulesheet
// @Tags eval
// @Accept json
// @Produce json
// @Param knowledgeBase path string false "knowledgeBase"
// @Param version path string false "version"
// @Param parameters body payloads.Eval true "Parameters"
// @Success 200 {string} string "ok"
// @Failure 400,404 {object} string
// @Failure 500 {object} string
// @Failure default {object} string
// @Router /eval/{knowledgeBase}/{version} [post]
// @Router /eval/{knowledgeBase} [post]
// @Router /eval [post]
func EvalHandler() gin.HandlerFunc {
return func(c *gin.Context) {
knowledgeBaseName := c.Param("knowledgeBase")
if knowledgeBaseName == "" {
knowledgeBaseName = DefaultKnowledgeBaseName
knowledgeBaseName = services.DefaultKnowledgeBaseName
}

version := c.Param("version")
if version == "" {
version = DefaultKnowledgeBaseVersion
version = services.DefaultKnowledgeBaseVersion
}

log.Printf("Eval with %s %s\n", knowledgeBaseName, version)
log.Debugf("Eval with %s %s\n", knowledgeBaseName, version)

loadMutex.Lock()

knowledgeBase := services.EvalService.GetKnowledgeLibrary().GetKnowledgeBase(knowledgeBaseName, version)

if !knowledgeBase.ContainsRuleEntry("DefaultValues") {
if !(len(knowledgeBase.RuleEntries) > 0) {

err := services.EvalService.LoadRemoteGRL(knowledgeBaseName, version)
if err != nil {
log.Errorf("Erro on load: %v", err)
c.String(http.StatusInternalServerError, "Error on load knowledgeBase and/or version")
// w.WriteHeader(http.StatusservicesUnavailable)
// encoder := json.NewEncoder(w)
// encoder.Encode(err)
loadMutex.Unlock()
return
}

knowledgeBase = services.EvalService.GetKnowledgeLibrary().GetKnowledgeBase(knowledgeBaseName, version)

if !knowledgeBase.ContainsRuleEntry("DefaultValues") {
if !(len(knowledgeBase.RuleEntries) > 0) {
c.Status(http.StatusNotFound)
fmt.Fprint(c.Writer, "KnowledgeBase or version not founded!")
loadMutex.Unlock()
Expand All @@ -66,31 +73,37 @@ func EvalHandler() gin.HandlerFunc {
loadMutex.Unlock()

decoder := json.NewDecoder(c.Request.Body)
var t map[string]interface{}
var t payloads.Eval
err := decoder.Decode(&t)
if err != nil {
log.Errorf("Erro on json decode: %v", err)
c.Status(http.StatusInternalServerError)
fmt.Fprint(c.Writer, "Error on json decode")
return
}
log.Println(t)
log.Debugln(t)

ctx := types.NewContextFromMap(t)

result, err := services.EvalService.Eval(ctx, knowledgeBase)
if err != nil {

log.Errorf("Error on eval: %v", err)
c.Status(http.StatusInternalServerError)
fmt.Fprint(c.Writer, "Error on eval")
return
}

// log.Print("Context:\n\t", ctx.GetEntries(), "\n\n")
// log.Print("Features:\n\t", result.GetFeatures(), "\n\n")
log.Debug("Context:\n\t", ctx.GetEntries(), "\n\n")
log.Debug("Features:\n\t", result.GetFeatures(), "\n\n")

responseCode := http.StatusOK

if result.Has("requiredParamErrors") {
responseCode = http.StatusBadRequest
}

c.JSON(http.StatusOK, result.GetFeatures())
//fmt.Fprintf(w, "%v", result)
c.JSON(responseCode, result.GetFeatures())
}

}
Loading

0 comments on commit 05cb00f

Please sign in to comment.