Skip to content

Commit

Permalink
feat: serve pimo via http server (#279)
Browse files Browse the repository at this point in the history
* feat(serve): add command

* feat(serve): add endpoint with payload

* feat(serve): fix string problem

* feat(serve): fix string problem

* feat(serve): fix string problem

* feat(serve): use option

* feat(serve): venom test

* feat(serve): fix venom test

* feat(serve): update changelog

* feat(serve): fix XIXO safe copy
  • Loading branch information
adrienaury authored Dec 23, 2023
1 parent 752f172 commit c0c663a
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Types of changes
## [1.21.0]

- `Added` new features `findInCSV` mask to get one or multiple csv lines which matched with Json entry value from CSV files.
- `Added` new flag `--serve` to start a HTTP server that apply masking to JSON body of requests.

## [1.20.0]

Expand Down
76 changes: 65 additions & 11 deletions cmd/pimo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package main
import (
"bytes"
"fmt"
"net/http"
netHttp "net/http"
"os"
"runtime"
Expand All @@ -32,6 +33,7 @@ import (
"github.com/cgi-fr/pimo/pkg/flow"
"github.com/cgi-fr/pimo/pkg/model"
"github.com/cgi-fr/pimo/pkg/statistics"
"github.com/labstack/echo/v4"
"github.com/mattn/go-isatty"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -68,6 +70,7 @@ var (
statsDestinationEnv = os.Getenv("PIMO_STATS_URL")
statsTemplateEnv = os.Getenv("PIMO_STATS_TEMPLATE")
xmlSubscriberName map[string]string
serve string
)

func main() {
Expand Down Expand Up @@ -109,6 +112,7 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
rootCmd.PersistentFlags().StringVar(&repeatWhile, "repeat-while", "", "mask each input repeatedly while the given condition is met")
rootCmd.PersistentFlags().StringVar(&statisticsDestination, "stats", statsDestinationEnv, "generate execution statistics in the specified dump file")
rootCmd.PersistentFlags().StringVar(&statsTemplate, "statsTemplate", statsTemplateEnv, "template string to format stats (to include them you have to specify them as `{{ .Stats }}` like `{\"software\":\"PIMO\",\"stats\":{{ .Stats }}}`)")
rootCmd.Flags().StringVar(&serve, "serve", "", "listen/respond to HTTP interface and port instead of stdin/stdout, <ip>:<port> or :<port> to listen to all local networks")

rootCmd.AddCommand(&cobra.Command{
Use: "jsonschema",
Expand Down Expand Up @@ -164,11 +168,23 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
}

parser.RegisterMapCallback(elementName, func(m map[string]string) (map[string]string, error) {
transformedData, err := ctx.ExecuteMap(m)
dictionary := make(map[string]any, len(m))
for k, v := range m {
dictionary[k] = v
}
transformedData, err := ctx.ExecuteMap(dictionary)
if err != nil {
return nil, err
}
return transformedData, nil
result := make(map[string]string, len(m))
for k, v := range transformedData {
stringValue, ok := v.(string)
if !ok {
return nil, fmt.Errorf("Result is not a string")
}
result[k] = stringValue
}
return result, nil
})
}
err := parser.Stream()
Expand Down Expand Up @@ -241,6 +257,7 @@ func run(cmd *cobra.Command) {
SkipLogFile: skipLogFile,
CachesToDump: cachesToDump,
CachesToLoad: cachesToLoad,
XMLCallback: len(serve) > 0,
}

var pdef model.Definition
Expand Down Expand Up @@ -269,21 +286,58 @@ func run(cmd *cobra.Command) {
os.Exit(1)
}

startTime := time.Now()
if len(serve) > 0 {
router := echo.New()
router.HideBanner = true
router.GET("/", httpHandler(ctx))
router.POST("/", httpHandler(ctx))
if err := router.Start(serve); err != nil {
log.Err(err).Msg("Failed to start server")
os.Exit(8)
}
} else {
startTime := time.Now()
stats, err := ctx.Execute(os.Stdout)
if err != nil {
log.Err(err).Msg("Cannot execute pipeline")
log.Warn().Int("return", stats.GetErrorCode()).Msg("End PIMO")
os.Exit(stats.GetErrorCode())
}

stats, err := ctx.Execute(os.Stdout)
if err != nil {
log.Err(err).Msg("Cannot execute pipeline")
log.Warn().Int("return", stats.GetErrorCode()).Msg("End PIMO")
os.Exit(stats.GetErrorCode())
duration := time.Since(startTime)
statistics.SetDuration(duration)
dumpStats(stats)
}

duration := time.Since(startTime)
statistics.SetDuration(duration)
dumpStats(stats)
os.Exit(0)
}

func httpHandler(pimo pimo.Context) func(ctx echo.Context) error {
return func(ctx echo.Context) error {
// dont't panic
defer func() error {
if r := recover(); r != nil {
log.Error().AnErr("panic", r.(error)).Msg("Recovering from panic in rest server.")
return ctx.String(http.StatusInternalServerError, r.(error).Error())
}
return nil
}() //nolint:errcheck

payload := map[string]any{}

if err := ctx.Bind(&payload); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

result, err := pimo.ExecuteMap(payload)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

return ctx.JSON(http.StatusOK, result)
}
}

func initLog() {
color := false
switch strings.ToLower(colormode) {
Expand Down
10 changes: 3 additions & 7 deletions internal/app/pimo/pimo.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func updateContext(counter int) {
over.MDC().Set("context", re.ReplaceAllString(context, fmt.Sprintf("[%d]", counter)))
}

func (ctx *Context) ExecuteMap(data map[string]string) (map[string]string, error) {
func (ctx *Context) ExecuteMap(data map[string]any) (map[string]any, error) {
input := model.NewDictionary()

for k, v := range data {
Expand All @@ -357,7 +357,7 @@ func (ctx *Context) ExecuteMap(data map[string]string) (map[string]string, error
return nil, err
}

newData := make(map[string]string)
newData := make(map[string]any)

if len(result) > 0 {
new_map, ok := result[0].(model.Dictionary)
Expand All @@ -366,11 +366,7 @@ func (ctx *Context) ExecuteMap(data map[string]string) (map[string]string, error
}
unordered := new_map.Unordered()
for k, v := range unordered {
stringValue, ok := v.(string)
if !ok {
return nil, fmt.Errorf("Result is not a string")
}
newData[k] = stringValue
newData[k] = v
}
return newData, nil
}
Expand Down
4 changes: 2 additions & 2 deletions internal/app/pimo/pimo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ func Test2BaliseIdentity(t *testing.T) {
err := ctx.Configure(cfg)
assert.Nil(t, err)

data := map[string]string{"name": "John"}
data := map[string]any{"name": "John"}
newData1, err := ctx.ExecuteMap(data)
assert.Nil(t, err)
newData2, err := ctx.ExecuteMap(data)
Expand Down Expand Up @@ -376,7 +376,7 @@ func TestExecuteMapWithAttributes(t *testing.T) {
err := ctx.Configure(cfg)
assert.Nil(t, err)

data := map[string]string{"name": "John", "name@age": "25"}
data := map[string]any{"name": "John", "name@age": "25"}

newData, err := ctx.ExecuteMap(data)

Expand Down
28 changes: 28 additions & 0 deletions test/suites/serve.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: random features
testcases:
- name: mask on HTTP
steps:
- script: rm -f masking.yml
- script: |-
cat > masking.yml <<EOF
version: "1"
masking:
- selector:
jsonpath: "name"
mask:
randomChoice:
- "Mickael"
- "Mathieu"
- "Marcel"
EOF
- type: exec
script: |
pimo -s 1 --serve :8080 &
sleep 2
curl -X POST -H 'Content-Type: application/json' -d '{"name":"Toto"}' -s http://localhost:8080
kill %1
assertions:
- result.code ShouldEqual 0
- result.systemout ShouldContainSubstring {"name":"Mickael"}
- result.systemerr ShouldBeEmpty

0 comments on commit c0c663a

Please sign in to comment.