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 #103 from bancodobrasil/feat/minio-resource-loader
Browse files Browse the repository at this point in the history
Feat/minio resource loader
  • Loading branch information
eliasfeijo authored Apr 2, 2024
2 parents fe996a2 + a622685 commit d6577d2
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 224 deletions.
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ example.json
*.out
vendor
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
66 changes: 58 additions & 8 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ import (
// - 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.
// - 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

Port string `mapstructure:"PORT"`
DefaultRules string `mapstructure:"FEATWS_RULLER_DEFAULT_RULES"`
Expand All @@ -46,7 +43,36 @@ type Config struct {
KnowledgeBaseVersionTTL int64 `mapstructure:"FEATWS_RULLER_KNOWLEDGE_BASE_VERSION_TTL"`
}

var config = &Config{}
// 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.
}

// 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 @@ -61,6 +87,12 @@ func LoadConfig() (err error) {
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", "")
Expand All @@ -80,13 +112,31 @@ func LoadConfig() (err error) {
}

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))
}

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.ResourceLoaderHeaders = make(http.Header)
resourceLoaderHeaders := strings.Split(config.ResourceLoaderHeadersStr, ",")
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 Down
4 changes: 2 additions & 2 deletions controllers/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ func newHandler() healthcheck.Handler {
cfg := config.GetConfig()
health.AddLivenessCheck("goroutine-threshold", goroutine.Count(100))

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
2 changes: 1 addition & 1 deletion 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 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
25 changes: 4 additions & 21 deletions docs/docs.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
// 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 @@ -276,9 +257,11 @@ var SwaggerInfo = &swag.Spec{
BasePath: "/api/v1",
Schemes: []string{},
Title: "FeatWS Ruler",
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",
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 d6577d2

Please sign in to comment.