Skip to content

Commit

Permalink
Merge pull request #3 from bancodobrasil/feat/featws-ruller-integration
Browse files Browse the repository at this point in the history
Feat/featws ruller integration
  • Loading branch information
ralphg6 authored Jun 30, 2023
2 parents 167b7fb + 1b8c912 commit 1f921ec
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 26 deletions.
6 changes: 5 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ JAMIE_SERVICE_LOADER_FILE_SYSTEM_PATH="./_data/"

# Seconds
# JAMIE_SERVICE_CACHE_CLOSED_TTL=3600
# JAMIE_SERVICE_CACHE_CURRENT_TTL=6069
# JAMIE_SERVICE_CACHE_CURRENT_TTL=6069

# FeatWS Ruller
JAMIE_SERVICE_FEATWS_RULLER_URL=http://featws-ruller:8000
JAMIE_SERVICE_FEATWS_RULLER_API_KEY=abc123
10 changes: 10 additions & 0 deletions api.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
GET http://localhost:8005/api/v1/menus/64623317-d00c-4508-8db3-613d1ed24638

###

POST http://localhost:8005/api/v1/menus/64623317-d00c-4508-8db3-613d1ed24638/eval
Content-Type: application/json

{
"idade": 18
}
68 changes: 68 additions & 0 deletions clients/featws/ruller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package featws

import (
"bytes"
"encoding/json"
"fmt"
"net/http"

"github.com/bancodobrasil/jamie-service/dtos"
log "github.com/sirupsen/logrus"
)

type EvalRequest map[string]interface{}

func NewEvalRequest(dto dtos.Process) EvalRequest {
return EvalRequest(dto)
}

type EvalPayload map[string]interface{}

type RullerClient struct {
Url string
ApiKey string
}

func NewRullerClient(url, apiKey string) *RullerClient {
return &RullerClient{
Url: url,
ApiKey: apiKey,
}
}

func (c *RullerClient) Eval(knowledgeBase string, version string, parameters EvalRequest) (*EvalPayload, error) {
url := fmt.Sprintf("%s/api/v1/eval/%s/%s", c.Url, knowledgeBase, version)
log.Debugf("Request url: %s", url)
parametersJson, err := json.Marshal(parameters)
if err != nil {
log.Errorf("Error marshaling parameters: %s", err)
return nil, err
}
body := []byte(string(parametersJson))
log.Debugf("Request body: %s", body)
bodyReader := bytes.NewReader(body)
request, err := http.NewRequest("POST", url, bodyReader)
if err != nil {
log.Errorf("Error creating request: %s", err)
return nil, err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("x-api-key", c.ApiKey)
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Errorf("Error making request: %s", err)
return nil, err
}
defer response.Body.Close()
log.Debugf("Response status: %s", response.Status)
log.Debugf("Response headers: %s", response.Header)
responseBody := &EvalPayload{}
err = json.NewDecoder(response.Body).Decode(responseBody)
if err != nil {
log.Errorf("Error decoding response: %s", err)
return nil, err
}
log.Debugf("Response body: %s", responseBody)
return responseBody, nil
}
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ type Config struct {
FileSystem *FileSystemLoader

S3 *S3Loader

// FeatWS Ruller Client
FeatWSRullerURL string `mapstructure:"JAMIE_SERVICE_FEATWS_RULLER_URL"`
FeatWSRullerAPIKey string `mapstructure:"JAMIE_SERVICE_FEATWS_RULLER_API_KEY"`
}

// Cache ...
Expand Down
2 changes: 1 addition & 1 deletion controllers/v1/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (ctrl *menu) EvalHandler() gin.HandlerFunc {
return
}

dto := dtos.NewEval(t)
dto := dtos.NewProcess(t)

menu, err := ctrl.service.Process(ctx, uuid, version, &dto)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions dtos/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package dtos

import v1 "github.com/bancodobrasil/jamie-service/payloads/v1"

// Eval ...
type Eval map[string]interface{}
// Process ...
type Process map[string]interface{}

// NewEval ...
func NewEval(payloads v1.Eval) Eval {
return Eval(payloads)
func NewProcess(payloads v1.Eval) Process {
return Process(payloads)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/bancodobrasil/goauth v1.0.2
github.com/bancodobrasil/goauth-gin v0.0.3
github.com/bancodobrasil/healthcheck v0.0.2-rc1
github.com/dlclark/regexp2 v1.10.0
github.com/gin-gonic/gin v1.8.1
github.com/gsdenys/healthcheck v0.0.0-20220412001953-64e5089fa0bc
github.com/minio/minio-go/v7 v7.0.49
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down
17 changes: 6 additions & 11 deletions services/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ package services

import (
"context"
"fmt"
"sync"
"time"
)

// Cache ...
type Cache interface {
Get(ctx context.Context, uuid string, version string) (interface{}, error)
Put(ctx context.Context, uuid string, version string, value interface{}, ttl time.Duration) error
Get(ctx context.Context, key interface{}) (interface{}, error)
Put(ctx context.Context, key interface{}, value interface{}, ttl time.Duration) error
Close() error
}

Expand All @@ -25,20 +24,16 @@ func NewCache() Cache {
}
}

func (c *cache) buildkey(uuid string, version string) string {
return fmt.Sprintf("%s-%s", uuid, version)
}

func (c *cache) Get(ctx context.Context, uuid string, version string) (interface{}, error) {
v, ok := c.eMap.Get(c.buildkey(uuid, version))
func (c *cache) Get(ctx context.Context, key interface{}) (interface{}, error) {
v, ok := c.eMap.Get(key)
if !ok {
return nil, nil
}
return v, nil
}

func (c *cache) Put(ctx context.Context, uuid string, version string, value interface{}, ttl time.Duration) error {
c.eMap.Put(c.buildkey(uuid, version), value, ttl)
func (c *cache) Put(ctx context.Context, key interface{}, value interface{}, ttl time.Duration) error {
c.eMap.Put(key, value, ttl)
return nil
}

Expand Down
116 changes: 107 additions & 9 deletions services/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,54 @@ package services
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strings"
"text/template"
"time"

"github.com/dlclark/regexp2"

"github.com/bancodobrasil/jamie-service/clients/featws"
"github.com/bancodobrasil/jamie-service/config"
"github.com/bancodobrasil/jamie-service/dtos"
"github.com/bancodobrasil/jamie-service/loaders"
"github.com/sirupsen/logrus"
)

type getCacheKey struct {
uuid string
version string
}

type processCacheKey struct {
uuid string
version string
payload *dtos.Process
}

type templateFormat struct {
Template string `json:"template"`
FeatWSVersion *string `json:"featws_version"`
}

// Menu ...
type Menu interface {
Get(ctx context.Context, uuid string, version string) (string, error)
Process(ctx context.Context, uuid string, version string, payload *dtos.Eval) (string, error)
Process(ctx context.Context, uuid string, version string, payload *dtos.Process) (string, error)
}

type menu struct {
cfg *config.Config
loadersManager loaders.Manager
cacheService Cache
rullerClient *featws.RullerClient
}

// NewMenu ...
func NewMenu(cfg *config.Config, loadersManager loaders.Manager, cacheService Cache) Menu {
return &menu{cfg: cfg, loadersManager: loadersManager, cacheService: cacheService}
rullerClient := featws.NewRullerClient(cfg.FeatWSRullerURL, cfg.FeatWSRullerAPIKey)
return &menu{cfg: cfg, loadersManager: loadersManager, cacheService: cacheService, rullerClient: rullerClient}
}

// load ...
Expand All @@ -52,7 +76,9 @@ func (s *menu) Get(ctx context.Context, uuid string, version string) (string, er
version = CurrentMenuVersion
}

content, err := s.cacheService.Get(ctx, uuid, version)
cacheKey := &getCacheKey{uuid: uuid, version: version}

content, err := s.cacheService.Get(ctx, cacheKey)
if err != nil {
return "", err
}
Expand All @@ -69,7 +95,7 @@ func (s *menu) Get(ctx context.Context, uuid string, version string) (string, er
ttl = time.Duration(s.cfg.Cache.CurrentTTL) * time.Second
}

err = s.cacheService.Put(ctx, uuid, version, content, ttl)
err = s.cacheService.Put(ctx, cacheKey, content, ttl)
if err != nil {
return "", err
}
Expand All @@ -82,24 +108,96 @@ func (s *menu) Get(ctx context.Context, uuid string, version string) (string, er
return content.(string), nil
}

func (s *menu) Process(ctx context.Context, uuid string, version string, dto *dtos.Eval) (string, error) {
templateContent, err := s.Get(ctx, uuid, version)
// Process ...
func (s *menu) Process(ctx context.Context, uuid string, version string, dto *dtos.Process) (string, error) {

cacheKey := &processCacheKey{uuid: uuid, version: version, payload: dto}

content, err := s.cacheService.Get(ctx, cacheKey)
if err != nil {
return "", err
}

if content != nil {
return content.(string), nil
}

tmpl, err := s.Get(ctx, uuid, version)
if err != nil {
return "", err
}

parsedTemplate := templateFormat{}

err = json.Unmarshal([]byte(tmpl), &parsedTemplate)
if err != nil {
return "", err
}

if parsedTemplate.FeatWSVersion == nil {
return parsedTemplate.Template, nil
}

evalResult, err := s.rullerClient.Eval("jamie-menu-"+uuid, *parsedTemplate.FeatWSVersion, featws.NewEvalRequest(*dto))
if err != nil {
return "", err
}

response, err := s.processTemplateConditions(
uuid,
parsedTemplate.Template,
evalResult,
)
if err != nil {
return "", err
}

err = s.cacheService.Put(ctx, cacheKey, response, time.Duration(s.cfg.Cache.ClosedTTL)*time.Second)
if err != nil {
return "", err
}

return response, nil
}

// processTemplateConditions ...
func (s *menu) processTemplateConditions(uuid string, templateContent string, evalResult *featws.EvalPayload) (string, error) {
tmpl, err := template.New(uuid).Parse(templateContent)
if err != nil {
return "", err
}

var buf bytes.Buffer

// TODO call featws
err = tmpl.Execute(&buf, nil)
err = tmpl.Execute(&buf, evalResult)
if err != nil {
return "", err
}

result, err := s.formatJson(buf.String())
if err != nil {
logrus.Errorf("Error formatting json: %s", err)
return "", err
}

return buf.String(), nil
return result, nil
}

// formatJson ...
func (s *menu) formatJson(json string) (string, error) {
r := regexp2.MustCompile("\\s", regexp2.None)
json, err := r.Replace(json, "", -1, -1)
if err != nil {
return json, err
}
if (strings.HasPrefix(json, "{") && strings.HasSuffix(json, "}")) ||
(strings.HasPrefix(json, "[") && strings.HasSuffix(json, "]")) {
r = regexp2.MustCompile("\\,(?!\\s*[\\{\"\\w])", regexp2.None)
json, err = r.Replace(json, "", -1, -1)
if err != nil {
return json, err
}
return json, err
}
return json, err
}

0 comments on commit 1f921ec

Please sign in to comment.