Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] tech dept #324

Merged
merged 11 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/bavix/features v1.0.0
github.com/bavix/gripmock-sdk-go v1.0.4
github.com/bavix/gripmock-ui v1.0.0-alpha5
github.com/bavix/gripmock/protogen v0.0.0-20240706174427-ef324cdfb46b
github.com/bavix/gripmock/protogen v0.0.0-20240706201937-fc1e72a8ad5f
github.com/cristalhq/base64 v0.1.2
github.com/goccy/go-yaml v1.11.3
github.com/google/uuid v1.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ github.com/bavix/gripmock-sdk-go v1.0.4 h1:FDBlusqVFoy5Yo49khztYqfVt9+NSEf1mIl1n
github.com/bavix/gripmock-sdk-go v1.0.4/go.mod h1:/1cmn8VuN6Pc7ttMejqXLYpvf1CJF08ezoEA9lJIZiU=
github.com/bavix/gripmock-ui v1.0.0-alpha5 h1:+2vWLZPeGGrpBSENWXIfyf6bwN+Pou+XucX0XlccLQo=
github.com/bavix/gripmock-ui v1.0.0-alpha5/go.mod h1:XEH4YYEKL+wEDtONntoWm6JxjbVWzl7XtDYztUTBfeA=
github.com/bavix/gripmock/protogen v0.0.0-20240706174427-ef324cdfb46b h1:YCLXlvREBDiqSZX8D4e0DtPQB0jm55cl4YsllmB0B4k=
github.com/bavix/gripmock/protogen v0.0.0-20240706174427-ef324cdfb46b/go.mod h1:ARIfXpB9cyL9jIr7C1yrhwnb3wCSrewPNLdyG4URmJk=
github.com/bavix/gripmock/protogen v0.0.0-20240706201937-fc1e72a8ad5f h1:B/nZWWeQRXb4SQFHQWw45cMAnZ6eu/T6TAkJFpTqLHw=
github.com/bavix/gripmock/protogen v0.0.0-20240706201937-fc1e72a8ad5f/go.mod h1:ARIfXpB9cyL9jIr7C1yrhwnb3wCSrewPNLdyG4URmJk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bufbuild/protocompile v0.14.0 h1:z3DW4IvXE5G/uTOnSQn+qwQQxvhckkTWLS/0No/o7KU=
github.com/bufbuild/protocompile v0.14.0/go.mod h1:N6J1NYzkspJo3ZwyL4Xjvli86XOj1xq4qAasUFxGups=
Expand Down
2 changes: 1 addition & 1 deletion go.work
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
go 1.22.2
go 1.22.5

use (
.
Expand Down
70 changes: 41 additions & 29 deletions internal/app/rest_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net/http"
"os"
"path"
"strings"
"sync/atomic"
"time"
Expand All @@ -30,10 +31,10 @@ var (
)

type RestServer struct {
ok atomic.Bool
stuber *stuber.Budgerigar
convertor *yaml2json.Convertor
caser cases.Caser
ok atomic.Bool
reflector *grpcreflector.GReflector
}

Expand All @@ -49,15 +50,12 @@ func NewRestServer(path string, reflector *grpcreflector.GReflector) (*RestServe

if path != "" {
server.readStubs(path) // TODO: someday you will need to rewrite this code
server.ok.Store(true)
}

return server, nil
}

func (h *RestServer) ServiceReady() {
h.ok.Store(true)
}

func (h *RestServer) ServicesList(w http.ResponseWriter, r *http.Request) {
services, err := h.reflector.Services(r.Context())
if err != nil {
Expand Down Expand Up @@ -288,49 +286,66 @@ func (h *RestServer) writeResponseError(err error, w http.ResponseWriter) {
}
}

func (h *RestServer) readStubs(path string) {
files, err := os.ReadDir(path)
// readStubs reads all the stubs from the given directory and its subdirectories,
// and adds them to the server's stub store.
// The stub files can be in yaml or json format.
// If a file is in yaml format, it will be converted to json format.
func (h *RestServer) readStubs(pathDir string) {
files, err := os.ReadDir(pathDir)
if err != nil {
log.Printf("Can't read stub from %s. %v\n", path, err)
log.Printf("can't read stubs from %s: %v", pathDir, err)

return
}

for _, file := range files {
// If the file is a directory, recursively read its stubs.
if file.IsDir() {
h.readStubs(path + "/" + file.Name())
h.readStubs(path.Join(pathDir, file.Name()))

continue
}

byt, err := os.ReadFile(path + "/" + file.Name())
// Read the stub file and add it to the server's stub store.
stubs, err := h.readStub(path.Join(pathDir, file.Name()))
if err != nil {
log.Printf("Error when reading file %s. %v. skipping...", file.Name(), err)
log.Printf("cant read stubs from %s: %v", file.Name(), err)

continue
}

if strings.HasSuffix(file.Name(), ".yaml") || strings.HasSuffix(file.Name(), ".yml") {
byt, err = h.convertor.Execute(file.Name(), byt)
if err != nil {
log.Printf("Error when unmarshalling file %s. %v. skipping...", file.Name(), err)

continue
}
}

var storageStubs []*stuber.Stub
h.stuber.PutMany(stubs...)
}
}

if err = jsondecoder.UnmarshalSlice(byt, &storageStubs); err != nil {
log.Printf("Error when unmarshalling file %s. %v %v. skipping...", file.Name(), string(byt), err)
// readStub reads a stub file and returns a slice of stubs.
// The stub file can be in yaml or json format.
// If the file is in yaml format, it will be converted to json format.
func (h *RestServer) readStub(path string) ([]*stuber.Stub, error) {
// Read the file
byt, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("error when reading file %s: %w", path, err)
}

continue
// If the file is in yaml format, convert it to json format
if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") {
byt, err = h.convertor.Execute(path, byt)
if err != nil {
return nil, fmt.Errorf("error when unmarshalling file %s: %w", path, err)
}
}

h.stuber.PutMany(storageStubs...)
// Unmarshal the json into a slice of stubs
var stubs []*stuber.Stub
if err := jsondecoder.UnmarshalSlice(byt, &stubs); err != nil {
return nil, fmt.Errorf("error when unmarshalling file %s: %v %w", path, string(byt), err)
}

return stubs, nil
}

// validateStub validates if the stub is valid or not.
func validateStub(stub *stuber.Stub) error {
if stub.Service == "" {
return ErrServiceIsMissing
Expand All @@ -353,12 +368,9 @@ func validateStub(stub *stuber.Stub) error {
return fmt.Errorf("input cannot be empty")
}

// TODO: validate all input case

if stub.Output.Error == "" && stub.Output.Data == nil && stub.Output.Code == nil {
// fixme
//nolint:goerr113,perfsprint
return fmt.Errorf("output can't be empty")
return fmt.Errorf("output cannot be empty")
}

return nil
Expand Down
21 changes: 11 additions & 10 deletions internal/pkg/muxmiddleware/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,34 @@ import (
"github.com/bavix/gripmock/pkg/jsondecoder"
)

// RequestLogger logs the request and response.
func RequestLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger := zerolog.Ctx(r.Context())
ww := &responseWriter{w: w, status: http.StatusOK}
ip, err := getIP(r)
now := time.Now()
start := time.Now()

bodyBytes, _ := io.ReadAll(r.Body)
r.Body.Close() // must close
r.Body.Close()
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))

next.ServeHTTP(ww, r)

event := logger.Info().Err(err).
event := logger.Info().
Err(err).
IPAddr("ip", ip).
Str("method", r.Method).
Str("url", r.URL.RequestURI())
Str("url", r.URL.RequestURI()).
Dur("elapsed", time.Since(start)).
Str("ua", r.UserAgent()).
Int("bytes", ww.bytesWritten).
Int("code", ww.status)

if err := jsondecoder.UnmarshalSlice(bodyBytes, nil); err == nil {
event.RawJSON("input", bodyBytes)
}

event.
Dur("elapsed", time.Since(now)).
Str("ua", r.UserAgent()).
Int("bytes", ww.bytes).
Int("code", ww.status).
Send()
event.Send()
})
}
26 changes: 13 additions & 13 deletions internal/pkg/muxmiddleware/resp_writer.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package muxmiddleware

import "net/http"
import (
"net/http"
)

type responseWriter struct {
w http.ResponseWriter

status int
bytes int
status int
bytesWritten int
}

func (r *responseWriter) Header() http.Header {
return r.w.Header()
func (rw *responseWriter) Header() http.Header {
return rw.w.Header()
}

func (r *responseWriter) Write(bytes []byte) (int, error) {
n, err := r.w.Write(bytes)

r.bytes += n
func (rw *responseWriter) Write(bytes []byte) (int, error) {
n, err := rw.w.Write(bytes)
rw.bytesWritten += n

return n, err
}

func (r *responseWriter) WriteHeader(statusCode int) {
r.w.WriteHeader(statusCode)

r.status = statusCode
func (rw *responseWriter) WriteHeader(statusCode int) {
rw.status = statusCode
rw.w.WriteHeader(statusCode)
}
22 changes: 12 additions & 10 deletions internal/pkg/muxmiddleware/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@ import (
"strings"
)

// getIP returns the IP address from the request headers.
// It returns the IP address from the X-Forwarded-For header if it exists,
// otherwise it returns the IP address from the RemoteAddr field.
func getIP(r *http.Request) (net.IP, error) {
ips := r.Header.Get("X-Forwarded-For")
splitIps := strings.Split(ips, ",")

if len(splitIps) > 0 {
netIP := net.ParseIP(splitIps[len(splitIps)-1])

if netIP != nil {
return netIP, nil
if forwardedFor := r.Header.Get("X-Forwarded-For"); forwardedFor != "" {
ips := strings.Split(forwardedFor, ",")
if len(ips) > 0 {
ip := strings.TrimSpace(ips[len(ips)-1])
if parsedIP := net.ParseIP(ip); parsedIP != nil {
return parsedIP, nil
}
}
}

ip, _, err := net.SplitHostPort(r.RemoteAddr)
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return nil, err
}

return net.ParseIP(ip), nil
return net.ParseIP(host), nil
}
Loading
Loading