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 #106 from bancodobrasil/develop
Browse files Browse the repository at this point in the history
Versão 2.2.0
  • Loading branch information
ralphg6 authored Aug 1, 2024
2 parents 690fe8f + 545948a commit d8101d1
Show file tree
Hide file tree
Showing 20 changed files with 529 additions and 368 deletions.
7 changes: 6 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ 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
FEATWS_RULLER_API_KEY=abc123
FEATWS_RULLER_API_KEY=abc123

FEATWS_RULLER_KNOWLEDGE_BASE_VERSION_TTL=5

GOAUTH_HANDLERS=api_key
GOAUTH_API_KEY_LIST=abc123
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
branches: [ develop, master ]

env:
LATEST_GO_VERSION: "1.19"
LATEST_GO_VERSION: "1.22"
GO111MODULE: "on"

jobs:
Expand All @@ -16,7 +16,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
go_version: ['1.19','1.20']
go_version: ['1.21','1.22']
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ example.json
*.env
*.out
vendor
api.http
test.json
__debug_bin*
.vscode/
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.19-alpine AS BUILD
FROM golang:1.22-alpine AS BUILD

WORKDIR /app

Expand All @@ -12,7 +12,7 @@ COPY . /app

RUN go build -o ruller

FROM alpine:3.15
FROM alpine:3.19

COPY --from=BUILD /app/ruller /bin/

Expand Down
8 changes: 8 additions & 0 deletions api.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#POST http://localhost:8000/api/v1/eval/mobilepf/latest
POST http://localhost:8000/api/v1/eval/jamie-menu-64623317-d00c-4508-8db3-613d1ed24638/
Content-Type: application/json
X-API-Key: abc123

{
"idade":13
}
81 changes: 68 additions & 13 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ import (
// - ResolverBridgeHeaders: This field will be used to store HTTP headers that will be sent along with requests to the resolver bridge URL. The `http.Header` type is a map of strings to slices of strings, representing the headers and their values.
// - ResolverBridgeHeadersStr: This property is a string representation of the HTTP headers that will be sent to the resolver bridge. It is used in conjunction with the ResolverBridgeHeaders property to set the headers for requests made to the resolver bridge. The headers can be specified as a JSON object in string format.
// - ExternalHost: This property represents the external host name or IP address of the server where the application is running. It is used to construct URLs for external resources and APIs.
// - AuthAPIKey: This property is used to store the API key for authentication purposes. It is used to authenticate requests made to the Ruller API.
// - KnowledgeBaseVersionTTL: This property is used to define the TTL of a KnowledgeBase Version when it's used a tag name version.
type Config struct {
ResourceLoaderType string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_TYPE"`
ResourceLoaderURL string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_URL"`
ResourceLoaderHeaders http.Header
ResourceLoaderHeadersStr string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_HEADERS"`
ResourceLoader *ResourceLoader

LogLevel string `mapstructure:"FEATWS_RULLER_LOG_LEVEL"`

Port string `mapstructure:"PORT"`
DefaultRules string `mapstructure:"FEATWS_RULLER_DEFAULT_RULES"`
Expand All @@ -43,10 +42,41 @@ type Config struct {

ExternalHost string `mapstructure:"EXTERNAL_HOST"`

AuthAPIKey string `mapstructure:"FEATWS_RULLER_API_KEY"`
KnowledgeBaseVersionTTL int64 `mapstructure:"FEATWS_RULLER_KNOWLEDGE_BASE_VERSION_TTL"`

GoroutineThreshold int64 `mapstructure:"FEATWS_RULLER_GOROUTINE_THRESHOLD"`
}

// ResourceLoader represents a generic resource loader that can be either HTTP or Minio type.
type ResourceLoader struct {
Type string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_TYPE"` // Type of resource loader.
HTTP *ResourceLoaderHTTP // Specific configurations for HTTP resource loader.
Minio *ResourceLoaderMinio // Specific configurations for Minio resource loader.
}

var config = &Config{}
// ResourceLoaderHTTP represents specific configurations for an HTTP resource loader.
type ResourceLoaderHTTP struct {
URL string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_HTTP_URL"` // URL of the HTTP resource loader.
Headers http.Header // HTTP Headers to be sent in the requests.
HeadersStr string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_HTTP_HEADERS"` // String representation of the HTTP headers.
}

// ResourceLoaderMinio represents specific configurations for a Minio resource loader.
type ResourceLoaderMinio struct {
Bucket string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_MINIO_BUCKET"` // Minio Bucket for loading resources.
Endpoint string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_MINIO_ENDPOINT"` // Minio server Endpoint.
AccessKey string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_MINIO_ACCESS_KEY"` // Minio Access Key.
SecretKey string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_MINIO_SECRET_KEY"` // Minio Secret Key.
UseSSL bool `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_MINIO_USE_SSL"` // Indicates whether SSL should be used in the connection.
PathTemplate string `mapstructure:"FEATWS_RULLER_RESOURCE_LOADER_MINIO_PATH_TEMPLATE"` // Path template for resources in Minio.
}

var config = &Config{
ResourceLoader: &ResourceLoader{
HTTP: &ResourceLoaderHTTP{},
Minio: &ResourceLoaderMinio{},
},
}

var loaded = false

Expand All @@ -58,34 +88,60 @@ func LoadConfig() (err error) {

viper.AutomaticEnv()

viper.SetDefault("FEATWS_RULLER_LOG_LEVEL", "INFO")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_TYPE", "http")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_URL", "")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_HEADERS", "")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_MINIO_BUCKET", "")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_MINIO_ENDPOINT", "")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_MINIO_ACCESS_KEY", "")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_MINIO_SECRET_KEY", "")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_MINIO_USE_SSL", "true")
viper.SetDefault("FEATWS_RULLER_RESOURCE_LOADER_MINIO_PATH_TEMPLATE", "{knowledgeBase}/{version}.grl")
viper.SetDefault("FEATWS_RULLER_RESOLVER_BRIDGE_URL", "")
viper.SetDefault("FEATWS_RULLER_RESOLVER_BRIDGE_HEADERS", "")
viper.SetDefault("FEATWS_RULLER_DEFAULT_RULES", "")
viper.SetDefault("PORT", "8000")
viper.SetDefault("FEATWS_DISABLE_SSL_VERIFY", false)
viper.SetDefault("EXTERNAL_HOST", "localhost:8000")
viper.SetDefault("FEATWS_RULLER_API_KEY", "")
viper.SetDefault("FEATWS_RULLER_KNOWLEDGE_BASE_VERSION_TTL", "300")
viper.SetDefault("FEATWS_RULLER_GOROUTINE_THRESHOLD", "200")

err = viper.ReadInConfig()
if err != nil {
if err2, ok := err.(*os.PathError); !ok {
err = err2
log.Errorf("Error on Load Config: %v", err)

return
}
}

err = viper.Unmarshal(config)
if err != nil {
panic(fmt.Sprintf("load config error: %s", err))
}

err = viper.Unmarshal(config.ResourceLoader)
if err != nil {
panic(fmt.Sprintf("load config http error: %s", err))
}

config.ResourceLoaderHeaders = make(http.Header)
resourceLoaderHeaders := strings.Split(config.ResourceLoaderHeadersStr, ",")
err = viper.Unmarshal(config.ResourceLoader.HTTP)
if err != nil {
panic(fmt.Sprintf("load config http error: %s", err))
}

err = viper.Unmarshal(config.ResourceLoader.Minio)
if err != nil {
panic(fmt.Sprintf("load config minio error: %s", err))
}

config.ResourceLoader.HTTP.Headers = make(http.Header)
resourceLoaderHeaders := strings.Split(config.ResourceLoader.HTTP.HeadersStr, ",")
for _, value := range resourceLoaderHeaders {
entries := strings.Split(value, ":")
if len(entries) == 2 {
config.ResourceLoaderHeaders.Set(entries[0], entries[1])
config.ResourceLoader.HTTP.Headers.Set(entries[0], entries[1])
}
}

Expand All @@ -97,7 +153,6 @@ func LoadConfig() (err error) {
config.ResolverBridgeHeaders.Set(entries[0], entries[1])
}
}

return
}

Expand Down
6 changes: 3 additions & 3 deletions controllers/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ var health = healthcheck.NewHandler()
// configuration.
func newHandler() healthcheck.Handler {
cfg := config.GetConfig()
health.AddLivenessCheck("goroutine-threshold", goroutine.Count(100))
health.AddLivenessCheck("goroutine-threshold", goroutine.Count(int(cfg.GoroutineThreshold)))

if cfg.ResourceLoaderURL != "" {
rawResourceLoaderURL := cfg.ResourceLoaderURL
if cfg.ResourceLoader.Type == "http" && cfg.ResourceLoader.HTTP.URL != "" {
rawResourceLoaderURL := cfg.ResourceLoader.HTTP.URL
resourceLoader, _ := url.Parse(rawResourceLoaderURL)

if resourceLoader.Scheme == "" {
Expand Down
8 changes: 4 additions & 4 deletions controllers/v1/evalHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func EvalHandler() gin.HandlerFunc {

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

knowledgeBase, requestError := services.EvalService.GetKnowledgeBase(knowledgeBaseName, version)
knowledgeBase, requestError := services.EvalService.GetKnowledgeBase(c, knowledgeBaseName, version)
if requestError != nil {
c.String(requestError.StatusCode, requestError.Message)
return
Expand All @@ -148,7 +148,7 @@ func EvalHandler() gin.HandlerFunc {
fmt.Fprint(c.Writer, "Error on json decode")
return
}
log.Debugln(t)
log.Traceln(t)

ctx := types.NewContextFromMap(t)
ctx.RawContext = c.Request.Context()
Expand All @@ -162,8 +162,8 @@ func EvalHandler() gin.HandlerFunc {
return
}

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

responseCode := http.StatusOK

Expand Down
21 changes: 11 additions & 10 deletions controllers/v1/evalHandler_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v1

import (
"context"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -49,7 +50,7 @@ type EvalServiceTestEvalHandlerWithoutKnowledgeBaseAndVersion struct {
// testing purposes. It checks if the `knowledgeBaseName` and `version` parameters passed to the method
// are equal to the default values defined in the `services` package. If they are not equal, it logs an
// error using the `testing.T` object and returns `nil`. If they are equal, it simply returns `nil`.
func (s EvalServiceTestEvalHandlerWithoutKnowledgeBaseAndVersion) LoadRemoteGRL(knowledgeBaseName string, version string) error {
func (s EvalServiceTestEvalHandlerWithoutKnowledgeBaseAndVersion) LoadRemoteGRL(ctx context.Context, knowledgeBaseName string, version string) error {
if knowledgeBaseName != services.DefaultKnowledgeBaseName || version != services.DefaultKnowledgeBaseVersion {
s.t.Error("Did not load the default")
}
Expand All @@ -66,7 +67,7 @@ func (s EvalServiceTestEvalHandlerWithoutKnowledgeBaseAndVersion) GetKnowledgeLi
return ast.NewKnowledgeLibrary()
}

func (s EvalServiceTestEvalHandlerWithoutKnowledgeBaseAndVersion) GetKnowledgeBase(knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
func (s EvalServiceTestEvalHandlerWithoutKnowledgeBaseAndVersion) GetKnowledgeBase(ctx context.Context, knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
return nil, &errors.RequestError{Message: "KnowledgeBase or version not found", StatusCode: 404}
}

Expand Down Expand Up @@ -111,7 +112,7 @@ type EvalServiceTestEvalHandlerLoadError struct {
// that might occur when loading a knowledge base or version. This function is used in the test case
// `TestEvalHandlerLoadError` to create a mock implementation of the `services.IEval` interface that
// returns an error when the `EvalHandler` function is called.
func (s EvalServiceTestEvalHandlerLoadError) LoadRemoteGRL(knowledgeBaseName string, version string) error {
func (s EvalServiceTestEvalHandlerLoadError) LoadRemoteGRL(ctx context.Context, knowledgeBaseName string, version string) error {
return fmt.Errorf("mock load error")
}

Expand All @@ -124,7 +125,7 @@ func (s EvalServiceTestEvalHandlerLoadError) GetKnowledgeLibrary() *ast.Knowledg
return ast.NewKnowledgeLibrary()
}

func (s EvalServiceTestEvalHandlerLoadError) GetKnowledgeBase(knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
func (s EvalServiceTestEvalHandlerLoadError) GetKnowledgeBase(ctx context.Context, knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
return nil, &errors.RequestError{Message: "Error on load knowledgeBase and/or version", StatusCode: 500}
}

Expand Down Expand Up @@ -168,7 +169,7 @@ type EvalServiceTestEvalHandlerWithDefaultKnowledgeBase struct {

// This method takes two parameters, `knowledgeBaseName` and `version`, but it does not perform any action and
// always returns `nil`.
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBase) LoadRemoteGRL(knowledgeBaseName string, version string) error {
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBase) LoadRemoteGRL(ctx context.Context, knowledgeBaseName string, version string) error {
return nil
}

Expand All @@ -187,7 +188,7 @@ func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBase) Eval(ctx *types.Cont
}

// Thiss a test function for the EvalHandler function with a default knowledge base.
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBase) GetKnowledgeBase(knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBase) GetKnowledgeBase(ctx context.Context, knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
return s.kl.GetKnowledgeBase(knowledgeBaseName, version), nil
}

Expand Down Expand Up @@ -249,7 +250,7 @@ type EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseAndWrongJSON struct {
// This method takes in two parameters `knowledgeBaseName` and `version` of type string and returns an
// error. In this implementation, the method does not perform any action and simply returns a nil
// error.
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseAndWrongJSON) LoadRemoteGRL(knowledgeBaseName string, version string) error {
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseAndWrongJSON) LoadRemoteGRL(ctx context.Context, knowledgeBaseName string, version string) error {
return nil
}

Expand All @@ -265,7 +266,7 @@ func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseAndWrongJSON) Eval(ctx
}

// This is a test that tests the EvalHandler function with a default knowledge base and wrong JSON input.
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseAndWrongJSON) GetKnowledgeBase(knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseAndWrongJSON) GetKnowledgeBase(ctx context.Context, knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
return s.kl.GetKnowledgeBase(knowledgeBaseName, version), nil
}

Expand Down Expand Up @@ -327,7 +328,7 @@ type EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseEvalError struct {
// method takes in two parameters `knowledgeBaseName` and `version` of type string and returns an
// error. In this implementation, the method always returns `nil`, indicating that there was no error
// in loading the remote knowledge base.
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseEvalError) LoadRemoteGRL(knowledgeBaseName string, version string) error {
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseEvalError) LoadRemoteGRL(ctx context.Context, knowledgeBaseName string, version string) error {
return nil
}

Expand All @@ -349,7 +350,7 @@ func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseEvalError) Eval(ctx *t

// This is a test that tests the EvalHandler function with a default knowledge base and
// an evaluation error.
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseEvalError) GetKnowledgeBase(knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
func (s EvalServiceTestEvalHandlerWithDefaultKnowledgeBaseEvalError) GetKnowledgeBase(ctx context.Context, knowledgeBaseName string, version string) (*ast.KnowledgeBase, *errors.RequestError) {
return s.kl.GetKnowledgeBase(knowledgeBaseName, version), nil
}
func TestEvalHandlerWithDefaultKnowledgeBaseEvalError(t *testing.T) {
Expand Down
23 changes: 3 additions & 20 deletions docs/docs.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Code generated by swaggo/swag. DO NOT EDIT.

// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs

import "github.com/swaggo/swag"
Expand Down Expand Up @@ -44,18 +43,6 @@ const docTemplate = `{
],
"summary": "Evaluate the rulesheet / Avaliação da folha de Regra",
"parameters": [
{
"type": "string",
"description": "knowledgeBase",
"name": "knowledgeBase",
"in": "path"
},
{
"type": "string",
"description": "version",
"name": "version",
"in": "path"
},
{
"description": "Parameters",
"name": "parameters",
Expand Down Expand Up @@ -125,12 +112,6 @@ const docTemplate = `{
"name": "knowledgeBase",
"in": "path"
},
{
"type": "string",
"description": "version",
"name": "version",
"in": "path"
},
{
"description": "Parameters",
"name": "parameters",
Expand Down Expand Up @@ -279,6 +260,8 @@ var SwaggerInfo = &swag.Spec{
Description: "O projeto Ruler é uma implementação do motor de regras [grule-rule-engine](https://github.com/hyperjumptech/grule-rule-engine), que é utilizado para avaliar regras no formato .grl . O Ruler permite que as regras definidas em arquivos .grl sejam avaliadas de maneira automática e eficiente, ajudando a automatizar as decisões tomadas pelo FeatWS. Isso possibilita que o sistema possa analisar e classificar grandes quantidades de informações de maneira rápida e precisa.\n\nAo utilizar as regras fornecidas pelo projeto Ruler, o FeatWS é capaz de realizar análises de regras em larga escala e fornecer resultados precisos e relevantes para seus usuários. Isso é especialmente importante em áreas como análise de sentimentos em mídias sociais, detecção de fraudes financeiras e análise de dados em geral.\n\nAntes de realizar os testes no Swagger, é necessário autorizar o acesso clicando no botão **Authorize**, ao lado, e inserindo a senha correspondente. Após inserir o campo **value** e clicar no botão **Authorize**, o Swagger estará disponível para ser utilizado.\n\nA seguir é explicado com mais detalhes sobre os endpoints:\n- **/Eval**: Esse endpoint é utilizado apenas para aplicações que possuem uma única folha de regra padrão.\n- **/Eval/{knowledgeBase}**: Nesse endpoint, é necessário informar o parâmetro com o nome da folha de regra desejada e, como resultado, será retornado a última versão da folha de regra correspondente.\n- **/Eval/{knowledgeBase}/{version}**: Nesse endpoint é necessário colocar o parâmetro do nome da folha de regra como também o número da versão da folha de regra que você deseja testar a regra.\n\n**Parameters / Parâmetros**\nNo **knowledgeBase**, você pode especificar o nome da folha de regras que deseja utilizar. Já o **version** você coloca a versão que você deseja avaliar. Em **Paramenter**, é possível enviar os parametros que você deseja testar na folha de regra.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}

func init() {
Expand Down
Loading

0 comments on commit d8101d1

Please sign in to comment.