From 103c6c3cc86218e269ece4d6403134a24c26ae97 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 27 Sep 2023 00:12:52 +0300 Subject: [PATCH 01/16] add headers support --- api/openapi/api.yaml | 36 +++++++++++++++ example/simple/client/main.go | 23 ++++++++++ example/simple/stub/simple3.yaml | 13 ++++++ internal/app/rest_server.go | 1 + internal/app/storage.go | 79 +++++++++++++++++++++++++------- internal/domain/rest/api.gen.go | 41 +++++++++++------ pkg/sdk/api.gen.go | 41 +++++++++++------ pkg/storage/stubs.go | 48 +++++++++++++------ protoc-gen-gripmock/server.tmpl | 31 ++++++++++--- 9 files changed, 247 insertions(+), 66 deletions(-) diff --git a/api/openapi/api.yaml b/api/openapi/api.yaml index 51de3302..a12c9d8b 100644 --- a/api/openapi/api.yaml +++ b/api/openapi/api.yaml @@ -198,6 +198,11 @@ components: method: type: string example: SayHello + headers: + type: object + additionalProperties: + type: string + x-go-type-skip-optional-pointer: true data: type: object x-go-type: interface{} @@ -208,6 +213,11 @@ components: - data - error properties: + headers: + type: object + additionalProperties: + type: string + x-go-type-skip-optional-pointer: true data: type: object x-go-type: interface{} @@ -239,6 +249,8 @@ components: method: type: string example: SayHello + headers: + $ref: '#/components/schemas/StubHeaders' input: $ref: '#/components/schemas/StubInput' output: @@ -258,6 +270,25 @@ components: type: object additionalProperties: true x-go-type-skip-optional-pointer: true + StubHeaders: + type: object + x-go-type-skip-optional-pointer: true + properties: + equals: + type: object + additionalProperties: + type: string + x-go-type-skip-optional-pointer: true + contains: + type: object + additionalProperties: + type: string + x-go-type-skip-optional-pointer: true + matches: + type: object + additionalProperties: + type: string + x-go-type-skip-optional-pointer: true StubOutput: type: object required: @@ -267,6 +298,11 @@ components: data: type: object additionalProperties: true + headers: + type: object + additionalProperties: + type: string + x-go-type-skip-optional-pointer: true error: type: string example: Message not found diff --git a/example/simple/client/main.go b/example/simple/client/main.go index 88dd9165..8456d818 100644 --- a/example/simple/client/main.go +++ b/example/simple/client/main.go @@ -10,6 +10,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/encoding/gzip" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" pb "github.com/bavix/gripmock/protogen/example/simple" @@ -73,6 +74,28 @@ func main() { } log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + md := metadata.New(map[string]string{"Authorization": "Basic dXNlcjp1c2Vy"}) + ctx = metadata.NewOutgoingContext(context.Background(), md) + + var headers metadata.MD + + name = "simple3" + r, err = c.SayHello(ctx, &pb.Request{Name: name}, grpc.Header(&headers)) + if err != nil { + log.Fatalf("error from grpc: %v", err) + } + if r.ReturnCode != 0 { + log.Fatalf("grpc server returned code: %d, expected code: %d", r.ReturnCode, 0) + } + header := headers["result"] + if len(header) == 0 { + log.Fatal("the service did not return headers") + } + if header[0] != "ok" { + log.Fatal("the service returned an incorrect header") + } + log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + name = "simple3" r, err = c.SayHello(context.Background(), &pb.Request{Name: name}, grpc.UseCompressor(gzip.Name)) if err != nil { diff --git a/example/simple/stub/simple3.yaml b/example/simple/stub/simple3.yaml index df2356d8..dd9e36a1 100644 --- a/example/simple/stub/simple3.yaml +++ b/example/simple/stub/simple3.yaml @@ -7,3 +7,16 @@ data: message: Hello Simple3 return_code: 3 +- service: Gripmock + method: SayHello + headers: + contains: + authorization: Basic dXNlcjp1c2Vy # user:user + input: + equals: + name: simple3 + output: + data: + message: Authorization OK + headers: + result: ok diff --git a/internal/app/rest_server.go b/internal/app/rest_server.go index a8a96c78..d22eb9b9 100644 --- a/internal/app/rest_server.go +++ b/internal/app/rest_server.go @@ -52,6 +52,7 @@ type findStubPayload struct { ID *uuid.UUID `json:"id,omitempty"` Service string `json:"service"` Method string `json:"method"` + Headers map[string]interface{} `json:"headers"` Data map[string]interface{} `json:"data"` } diff --git a/internal/app/storage.go b/internal/app/storage.go index 61c52321..93e70467 100644 --- a/internal/app/storage.go +++ b/internal/app/storage.go @@ -13,11 +13,15 @@ import ( "github.com/bavix/gripmock/pkg/storage" ) +var ErrNotFound = errors.New("not found") + type matchFunc func(interface{}, interface{}) bool type closeMatch struct { - rule string - expect map[string]interface{} + rule string + expect map[string]interface{} + headerRule string + headerExpect map[string]interface{} } func findStub(stubStorage *storage.StubStorage, stub *findStubPayload) (*storage.Output, error) { @@ -41,36 +45,79 @@ func findStub(stubStorage *storage.StubStorage, stub *findStubPayload) (*storage } if stub.ID != nil { + stubStorage.MarkUsed(stubs[0].ID) + return &stubs[0].Output, nil } var closestMatch []closeMatch for _, strange := range stubs { - if expect := strange.Input.Equals; expect != nil { - closestMatch = append(closestMatch, closeMatch{"equals", expect}) - if equals(stub.Data, expect) { - return &strange.Output, nil + cmpData, cmpDataErr := inputCmp(strange.Input, stub.Data) + if cmpDataErr != nil { + if cmpData != nil { + closestMatch = append(closestMatch, *cmpData) } - } - if expect := strange.Input.Contains; expect != nil { - closestMatch = append(closestMatch, closeMatch{"contains", expect}) - if contains(strange.Input.Contains, stub.Data) { - return &strange.Output, nil - } + continue } - if expect := strange.Input.Matches; expect != nil { - closestMatch = append(closestMatch, closeMatch{"matches", expect}) - if matches(strange.Input.Matches, stub.Data) { - return &strange.Output, nil + if strange.CheckHeaders() { + if cmpHeaders, cmpHeadersErr := inputCmp(strange.Headers, stub.Headers); cmpHeadersErr != nil { + if cmpHeaders != nil { + closestMatch = append(closestMatch, closeMatch{ + rule: cmpData.rule, + expect: cmpData.expect, + headerRule: cmpHeaders.rule, + headerExpect: cmpHeaders.expect, + }) + } + + continue } } + + stubStorage.MarkUsed(strange.ID) + + return &strange.Output, nil } return nil, stubNotFoundError(stub, closestMatch) } +func inputCmp(input storage.Input, data map[string]interface{}) (*closeMatch, error) { + if expect := input.Equals; expect != nil { + closeMatchVal := closeMatch{rule: "equals", expect: expect} + + if equals(input.Equals, data) { + return &closeMatchVal, nil + } + + return &closeMatchVal, ErrNotFound + } + + if expect := input.Contains; expect != nil { + closeMatchVal := closeMatch{rule: "contains", expect: expect} + + if contains(input.Contains, data) { + return &closeMatchVal, nil + } + + return &closeMatchVal, ErrNotFound + } + + if expect := input.Matches; expect != nil { + closeMatchVal := closeMatch{rule: "matches", expect: expect} + + if matches(input.Matches, data) { + return &closeMatchVal, nil + } + + return &closeMatchVal, ErrNotFound + } + + return nil, ErrNotFound +} + func stubNotFoundError(stub *findStubPayload, closestMatches []closeMatch) error { template := fmt.Sprintf("Can't find stub \n\nService: %s \n\nMethod: %s \n\nInput\n\n", stub.Service, stub.Method) expectString, err := json.MarshalIndent(stub.Data, "", "\t") diff --git a/internal/domain/rest/api.gen.go b/internal/domain/rest/api.gen.go index f7541e72..fb939ebd 100644 --- a/internal/domain/rest/api.gen.go +++ b/internal/domain/rest/api.gen.go @@ -29,26 +29,36 @@ type MessageOK struct { // SearchRequest defines model for SearchRequest. type SearchRequest struct { - Data interface{} `json:"data"` - Id *ID `json:"id,omitempty"` - Method string `json:"method"` - Service string `json:"service"` + Data interface{} `json:"data"` + Headers map[string]string `json:"headers,omitempty"` + Id *ID `json:"id,omitempty"` + Method string `json:"method"` + Service string `json:"service"` } // SearchResponse defines model for SearchResponse. type SearchResponse struct { - Code *codes.Code `json:"code,omitempty"` - Data interface{} `json:"data"` - Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` + Data interface{} `json:"data"` + Error string `json:"error"` + Headers map[string]string `json:"headers,omitempty"` } // Stub defines model for Stub. type Stub struct { - Id *ID `json:"id,omitempty"` - Input StubInput `json:"input"` - Method string `json:"method"` - Output StubOutput `json:"output"` - Service string `json:"service"` + Headers StubHeaders `json:"headers,omitempty"` + Id *ID `json:"id,omitempty"` + Input StubInput `json:"input"` + Method string `json:"method"` + Output StubOutput `json:"output"` + Service string `json:"service"` +} + +// StubHeaders defines model for StubHeaders. +type StubHeaders struct { + Contains map[string]string `json:"contains,omitempty"` + Equals map[string]string `json:"equals,omitempty"` + Matches map[string]string `json:"matches,omitempty"` } // StubInput defines model for StubInput. @@ -63,9 +73,10 @@ type StubList = []Stub // StubOutput defines model for StubOutput. type StubOutput struct { - Code *codes.Code `json:"code,omitempty"` - Data map[string]interface{} `json:"data"` - Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` + Data map[string]interface{} `json:"data"` + Error string `json:"error"` + Headers map[string]string `json:"headers,omitempty"` } // AddStubJSONBody defines parameters for AddStub. diff --git a/pkg/sdk/api.gen.go b/pkg/sdk/api.gen.go index 0f33ba29..912b9a7f 100644 --- a/pkg/sdk/api.gen.go +++ b/pkg/sdk/api.gen.go @@ -33,26 +33,36 @@ type MessageOK struct { // SearchRequest defines model for SearchRequest. type SearchRequest struct { - Data interface{} `json:"data"` - Id *ID `json:"id,omitempty"` - Method string `json:"method"` - Service string `json:"service"` + Data interface{} `json:"data"` + Headers map[string]string `json:"headers,omitempty"` + Id *ID `json:"id,omitempty"` + Method string `json:"method"` + Service string `json:"service"` } // SearchResponse defines model for SearchResponse. type SearchResponse struct { - Code *codes.Code `json:"code,omitempty"` - Data interface{} `json:"data"` - Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` + Data interface{} `json:"data"` + Error string `json:"error"` + Headers map[string]string `json:"headers,omitempty"` } // Stub defines model for Stub. type Stub struct { - Id *ID `json:"id,omitempty"` - Input StubInput `json:"input"` - Method string `json:"method"` - Output StubOutput `json:"output"` - Service string `json:"service"` + Headers StubHeaders `json:"headers,omitempty"` + Id *ID `json:"id,omitempty"` + Input StubInput `json:"input"` + Method string `json:"method"` + Output StubOutput `json:"output"` + Service string `json:"service"` +} + +// StubHeaders defines model for StubHeaders. +type StubHeaders struct { + Contains map[string]string `json:"contains,omitempty"` + Equals map[string]string `json:"equals,omitempty"` + Matches map[string]string `json:"matches,omitempty"` } // StubInput defines model for StubInput. @@ -67,9 +77,10 @@ type StubList = []Stub // StubOutput defines model for StubOutput. type StubOutput struct { - Code *codes.Code `json:"code,omitempty"` - Data map[string]interface{} `json:"data"` - Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` + Data map[string]interface{} `json:"data"` + Error string `json:"error"` + Headers map[string]string `json:"headers,omitempty"` } // AddStubJSONBody defines parameters for AddStub. diff --git a/pkg/storage/stubs.go b/pkg/storage/stubs.go index a6fed21e..25322f91 100644 --- a/pkg/storage/stubs.go +++ b/pkg/storage/stubs.go @@ -16,6 +16,7 @@ type Stub struct { ID *uuid.UUID `json:"id,omitempty"` Service string `json:"service"` Method string `json:"method"` + Headers Input `json:"headers"` Input Input `json:"input"` Output Output `json:"output"` } @@ -29,6 +30,10 @@ func (s *Stub) GetID() uuid.UUID { return *s.ID } +func (s *Stub) CheckHeaders() bool { + return (len(s.Headers.Equals) + len(s.Headers.Matches) + len(s.Headers.Contains)) > 0 +} + type Input struct { Equals map[string]interface{} `json:"equals"` Contains map[string]interface{} `json:"contains"` @@ -36,15 +41,21 @@ type Input struct { } type Output struct { - Data map[string]interface{} `json:"data"` - Error string `json:"error"` - Code *codes.Code `json:"code,omitempty"` + Headers map[string]string `json:"headers"` + Data map[string]interface{} `json:"data"` + Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` } type storage struct { - ID uuid.UUID - Input Input - Output Output + ID uuid.UUID + Headers Input + Input Input + Output Output +} + +func (s *storage) CheckHeaders() bool { + return (len(s.Headers.Equals) + len(s.Headers.Matches) + len(s.Headers.Contains)) > 0 } type StubStorage struct { @@ -140,20 +151,27 @@ func (r *StubStorage) ItemsBy(service, method string, ID *uuid.UUID) ([]storage, return nil, err } + var resultWithHeaders []storage var result []storage for obj := it.Next(); obj != nil; obj = it.Next() { stub := obj.(*Stub) - r.used[stub.GetID()] = struct{}{} - result = append(result, storage{ - ID: stub.GetID(), - Input: stub.Input, - Output: stub.Output, - }) + s := storage{ + ID: stub.GetID(), + Headers: stub.Headers, + Input: stub.Input, + Output: stub.Output, + } + + if stub.CheckHeaders() { + resultWithHeaders = append(resultWithHeaders, s) + } else { + result = append(result, s) + } } - return result, nil + return append(resultWithHeaders, result...), nil } func (r *StubStorage) Unused() []Stub { @@ -187,6 +205,10 @@ func (r *StubStorage) Unused() []Stub { return result } +func (r *StubStorage) MarkUsed(id uuid.UUID) { + r.used[id] = struct{}{} +} + func (r *StubStorage) Stubs() []Stub { txn := r.db.Txn(false) defer txn.Abort() diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index 54891168..c9b2178f 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -15,6 +15,7 @@ import ( jsonpb "google.golang.org/protobuf/encoding/protojson" "golang.org/x/net/context" "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/reflection" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/grpc/codes" @@ -80,7 +81,8 @@ type {{.Name}} struct{ {{ define "standard_method" }} func (s *{{.ServiceName}}) {{.Name}}(ctx context.Context, in *{{.Input}}) (*{{.Output}},error){ out := &{{.Output}}{} - err := findStub(ctx, "{{.ServiceName}}", "{{.Name}}", in, out) + md, _ := metadata.FromIncomingContext(ctx) + err := findStub(ctx, "{{.ServiceName}}", "{{.Name}}", md, in, out) return out, err } {{ end }} @@ -88,8 +90,10 @@ func (s *{{.ServiceName}}) {{.Name}}(ctx context.Context, in *{{.Input}}) (*{{.O {{ define "server_stream_method" }} func (s *{{.ServiceName}}) {{.Name}}(in *{{.Input}},srv {{.SvcPackage}}{{.ServiceName}}_{{.Name}}Server) error { out := &{{.Output}}{} - err := findStub(srv.Context(), "{{.ServiceName}}", "{{.Name}}", in, out) - if err!=nil { + ctx := srv.Context() + md, _ := metadata.FromIncomingContext(ctx) + err := findStub(ctx, "{{.ServiceName}}", "{{.Name}}", md, in, out) + if err != nil { return err } @@ -100,12 +104,14 @@ func (s *{{.ServiceName}}) {{.Name}}(in *{{.Input}},srv {{.SvcPackage}}{{.Servic {{ define "client_stream_method"}} func (s *{{.ServiceName}}) {{.Name}}(srv {{.SvcPackage}}{{.ServiceName}}_{{.Name}}Server) error { out := &{{.Output}}{} + ctx := srv.Context() + md, _ := metadata.FromIncomingContext(ctx) for { input,err := srv.Recv() if errors.Is(err, io.EOF) { return srv.SendAndClose(out) } - err = findStub(srv.Context(), "{{.ServiceName}}","{{.Name}}",input,out) + err = findStub(ctx, "{{.ServiceName}}","{{.Name}}",md,input,out) if err != nil { return err } @@ -115,6 +121,8 @@ func (s *{{.ServiceName}}) {{.Name}}(srv {{.SvcPackage}}{{.ServiceName}}_{{.Name {{ define "bidirectional_method"}} func (s *{{.ServiceName}}) {{.Name}}(srv {{.SvcPackage}}{{.ServiceName}}_{{.Name}}Server) error { + ctx := srv.Context() + md, _ := metadata.FromIncomingContext(ctx) for { in, err := srv.Recv() if errors.Is(err, io.EOF) { @@ -125,7 +133,7 @@ func (s *{{.ServiceName}}) {{.Name}}(srv {{.SvcPackage}}{{.ServiceName}}_{{.Name } out := &{{.Output}}{} - err = findStub(srv.Context(), "{{.ServiceName}}","{{.Name}}",in,out) + err = findStub(ctx, "{{.ServiceName}}","{{.Name}}", md, in, out) if err != nil { return err } @@ -154,16 +162,22 @@ type response struct { Error string `json:"error"` } -func findStub(ctx context.Context, service, method string, in, out protoreflect.ProtoMessage) error { +func findStub(ctx context.Context, service, method string, md metadata.MD, in, out protoreflect.ProtoMessage) error { api, err := sdk.NewClientWithResponses(fmt.Sprintf("http://localhost%s/api", HTTP_PORT)) if err != nil { return err } + headers := make(map[string]string, len(md)) + for h, v := range md { + headers[h] = strings.Join(v, ";") + } + searchStub, err := api.SearchStubsWithResponse(ctx, sdk.SearchStubsJSONRequestBody{ Service: service, Method: method, - Data: in, + Headers: headers, + Data: in, }) if err != nil { return err @@ -188,6 +202,9 @@ func findStub(ctx context.Context, service, method string, in, out protoreflect. return err } + mdResp := metadata.New(searchStub.JSON200.Headers) + grpc.SetHeader(ctx, mdResp) + return jsonpb.Unmarshal(data, out) } {{ end }} From ff31f1f5ec0ca1a75f9a516448bbdfcab7b9408f Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 00:09:43 +0300 Subject: [PATCH 02/16] fix unit workflow --- .github/workflows/unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 1be67025..b343857b 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies run: go get . - name: Test with Go - run: go test -json > TestResults-${{ matrix.go-version }}.json + run: go test -json ./... > TestResults-${{ matrix.go-version }}.json - name: Upload Go test results uses: actions/upload-artifact@v3 with: From cbe6ca42233023d78954b1065df2b378a4c674d8 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 00:13:56 +0300 Subject: [PATCH 03/16] update workflow --- .github/workflows/unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index b343857b..283d0cee 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies run: go get . - name: Test with Go - run: go test -json ./... > TestResults-${{ matrix.go-version }}.json + run: go test -json ./... 2>&1 | tee -a TestResults-${{ matrix.go-version }}.json - name: Upload Go test results uses: actions/upload-artifact@v3 with: From cb6a61e74a8f899ded367a49886694cf31274319 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 00:26:04 +0300 Subject: [PATCH 04/16] fix units --- stub/api_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/stub/api_test.go b/stub/api_test.go index 318dfd6b..d34977a8 100644 --- a/stub/api_test.go +++ b/stub/api_test.go @@ -55,7 +55,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodGet, "/api/stubs", nil) }, handler: api.ListStubs, - expect: "[{\"id\":\"43739ed8-2810-4f57-889b-4d3ff5795bce\",\"service\":\"Testing\",\"method\":\"TestMethod\",\"input\":{\"equals\":{\"Hola\":\"Mundo\"},\"contains\":null,\"matches\":null},\"output\":{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}}]", + expect: "[{\"id\":\"43739ed8-2810-4f57-889b-4d3ff5795bce\",\"service\":\"Testing\",\"method\":\"TestMethod\",\"headers\":{\"equals\":null,\"contains\":null,\"matches\":null},\"input\":{\"equals\":{\"Hola\":\"Mundo\"},\"contains\":null,\"matches\":null},\"output\":{\"data\":{\"Hello\":\"World\"},\"error\":\"\",\"headers\":null}}]", }, { name: "unused stubs (all stubs)", @@ -63,7 +63,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodGet, "/api/stubs/unused", nil) }, handler: api.ListUnusedStubs, - expect: "[{\"id\":\"43739ed8-2810-4f57-889b-4d3ff5795bce\",\"service\":\"Testing\",\"method\":\"TestMethod\",\"input\":{\"equals\":{\"Hola\":\"Mundo\"},\"contains\":null,\"matches\":null},\"output\":{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}}]", + expect: "[{\"id\":\"43739ed8-2810-4f57-889b-4d3ff5795bce\",\"service\":\"Testing\",\"method\":\"TestMethod\",\"headers\":{\"equals\":null,\"contains\":null,\"matches\":null},\"input\":{\"equals\":{\"Hola\":\"Mundo\"},\"contains\":null,\"matches\":null},\"output\":{\"data\":{\"Hello\":\"World\"},\"error\":\"\",\"headers\":null}}]", }, { name: "find stub equals", @@ -73,7 +73,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodPost, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\",\"headers\":null}\n", }, { name: "unused stubs (zero)", @@ -91,7 +91,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodPost, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\",\"headers\":null}\n", }, { name: "add nested stub equals", @@ -134,7 +134,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodPost, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\",\"headers\":null}\n", }, { name: "add stub contains", @@ -177,7 +177,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodGet, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"hello\":\"world\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"hello\":\"world\"},\"error\":\"\",\"headers\":null}\n", }, { name: "add nested stub contains", @@ -304,7 +304,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodPost, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":null,\"error\":\"error msg\",\"code\":3}\n", + expect: "{\"data\":null,\"error\":\"error msg\",\"code\":3,\"headers\":null}\n", }, { @@ -353,7 +353,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodPost, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":null,\"error\":\"error msg\"}\n", + expect: "{\"data\":null,\"error\":\"error msg\",\"headers\":null}\n", }, { name: "find nested stub contains", @@ -376,7 +376,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodGet, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"hello\":\"world\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"hello\":\"world\"},\"error\":\"\",\"headers\":null}\n", }, { name: "add stub matches regex", @@ -416,7 +416,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodGet, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"reply\":\"OK\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"reply\":\"OK\"},\"error\":\"\",\"headers\":null}\n", }, { name: "add nested stub matches regex", @@ -471,7 +471,7 @@ func TestStub(t *testing.T) { return httptest.NewRequest(http.MethodGet, "/api/stubs/search", bytes.NewReader([]byte(payload))) }, handler: api.SearchStubs, - expect: "{\"data\":{\"reply\":\"OK\"},\"error\":\"\"}\n", + expect: "{\"data\":{\"reply\":\"OK\"},\"error\":\"\",\"headers\":null}\n", }, { name: "error find stub contains", @@ -509,7 +509,7 @@ func TestStub(t *testing.T) { res, err := io.ReadAll(wrt.Result().Body) assert.NoError(t, err) - require.JSONEq(t, v.expect, string(res)) + require.JSONEq(t, v.expect, string(res), string(res)) }) } From cb07b82eeef8566bcf758ff68ee352c0f97f66fe Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 00:28:19 +0300 Subject: [PATCH 05/16] lint fix --- pkg/storage/uuid_field_index.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/storage/uuid_field_index.go b/pkg/storage/uuid_field_index.go index 987d21b1..e3a60eee 100644 --- a/pkg/storage/uuid_field_index.go +++ b/pkg/storage/uuid_field_index.go @@ -67,7 +67,7 @@ func (u *UUIDFieldIndex) parseString(s string, enforceLength bool) ([]byte, erro if enforceLength && l != 36 { return nil, fmt.Errorf("UUID must be 36 characters") } else if l > 36 { - return nil, fmt.Errorf("Invalid UUID length. UUID have 36 characters; got %d", l) + return nil, fmt.Errorf("invalid UUID length. UUID have 36 characters; got %d", l) } hyphens := strings.Count(s, "-") @@ -79,12 +79,12 @@ func (u *UUIDFieldIndex) parseString(s string, enforceLength bool) ([]byte, erro sanitized := strings.Replace(s, "-", "", -1) sanitizedLength := len(sanitized) if sanitizedLength%2 != 0 { - return nil, fmt.Errorf("Input (without hyphens) must be even length") + return nil, fmt.Errorf("input (without hyphens) must be even length") } dec, err := hex.DecodeString(sanitized) if err != nil { - return nil, fmt.Errorf("Invalid UUID: %v", err) + return nil, fmt.Errorf("invalid UUID: %v", err) } return dec, nil From 5c5fe125c851faba43963796c1ed9a0bbfb49e32 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 12:44:12 +0300 Subject: [PATCH 06/16] bugfix --- example/simple/client/main.go | 49 ++++++++++++++++++++++++++++++++ example/simple/stub/simple3.yaml | 28 ++++++++++++++++++ gripmock.go | 4 +-- protoc-gen-gripmock/server.tmpl | 11 ++++++- stub/stub.go | 3 +- 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/example/simple/client/main.go b/example/simple/client/main.go index 8456d818..0a3db1e7 100644 --- a/example/simple/client/main.go +++ b/example/simple/client/main.go @@ -96,6 +96,55 @@ func main() { } log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + md2 := metadata.New(map[string]string{"Authorization": "Basic dXNlcjp1c2Vy", "ab": "blue"}) + ctx = metadata.NewOutgoingContext(context.Background(), md2) + + var headers2 metadata.MD + + name = "simple3" + r, err = c.SayHello(ctx, &pb.Request{Name: name}, grpc.Header(&headers2)) + if err != nil { + log.Fatalf("error from grpc: %v", err) + } + if r.ReturnCode != 0 { + log.Fatalf("grpc server returned code: %d, expected code: %d", r.ReturnCode, 0) + } + if _, ok := headers2["result"]; !ok { + log.Fatal("header key `result` not found") + } + if len(headers2["result"]) != 3 { + log.Fatalf("the service did not return headers %+v", headers2) + } + if headers2["result"][0] != "blue" && headers2["result"][1] != "red" && headers2["result"][2] != "none" { + log.Fatal("the service returned an incorrect header") + } + log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + + md3 := metadata.New(map[string]string{"Authorization": "Basic dXNlcjp1c2Vy", "ab": "red"}) + ctx = metadata.NewOutgoingContext(context.Background(), md3) + + var headers3 metadata.MD + + name = "simple3" + r, err = c.SayHello(ctx, &pb.Request{Name: name}, grpc.Header(&headers3)) + if err != nil { + log.Fatalf("error from grpc: %v", err) + } + if r.ReturnCode != 0 { + log.Fatalf("grpc server returned code: %d, expected code: %d", r.ReturnCode, 0) + } + if _, ok := headers3["result"]; !ok { + log.Fatal("header key `result` not found") + } + headers3.Get("result") + if len(headers3["result"]) != 3 { + log.Fatalf("the service did not return headers %+v", headers3) + } + if headers2["result"][0] != "red" && headers2["result"][1] != "blue" && headers2["result"][2] != "none" { + log.Fatal("the service returned an incorrect header") + } + log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + name = "simple3" r, err = c.SayHello(context.Background(), &pb.Request{Name: name}, grpc.UseCompressor(gzip.Name)) if err != nil { diff --git a/example/simple/stub/simple3.yaml b/example/simple/stub/simple3.yaml index dd9e36a1..57164105 100644 --- a/example/simple/stub/simple3.yaml +++ b/example/simple/stub/simple3.yaml @@ -20,3 +20,31 @@ message: Authorization OK headers: result: ok +- service: Gripmock + method: SayHello + headers: + contains: + authorization: Basic dXNlcjp1c2Vy + ab: blue + input: + equals: + name: simple3 + output: + data: + message: Blue OK + headers: + result: blue;red;none +- service: Gripmock + method: SayHello + headers: + contains: + authorization: Basic dXNlcjp1c2Vy + ab: red + input: + equals: + name: simple3 + output: + data: + message: Red OK + headers: + result: red;blue;none diff --git a/gripmock.go b/gripmock.go index ed516019..0ba40351 100644 --- a/gripmock.go +++ b/gripmock.go @@ -24,9 +24,9 @@ import ( func main() { outputPointer := flag.String("o", "", "directory to output server.go. Default is $GOPATH/src/grpc/") grpcPort := flag.String("grpc-port", "4770", "Port of gRPC tcp server") - grpcBindAddr := flag.String("grpc-listen", "", "Adress the gRPC server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") + grpcBindAddr := flag.String("grpc-listen", "0.0.0.0", "Adress the gRPC server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") adminport := flag.String("admin-port", "4771", "Port of stub admin server") - adminBindAddr := flag.String("admin-listen", "", "Adress the admin server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") + adminBindAddr := flag.String("admin-listen", "0.0.0.0", "Adress the admin server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") stubPath := flag.String("stub", "", "Path where the stub files are (Optional)") imports := flag.String("imports", "/protobuf", "comma separated imports path. default path /protobuf is where gripmock Dockerfile install WKT protos") // for backwards compatibility diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index c9b2178f..626a5aae 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -202,7 +202,16 @@ func findStub(ctx context.Context, service, method string, md metadata.MD, in, o return err } - mdResp := metadata.New(searchStub.JSON200.Headers) + mdResp := make(metadata.MD, len(searchStub.JSON200.Headers)) + for k, v := range searchStub.JSON200.Headers { + splits := strings.Split(v, ";") + for i, s := range splits { + splits[i] = strings.TrimSpace(s) + } + + mdResp[k] = splits + } + grpc.SetHeader(ctx, mdResp) return jsonpb.Unmarshal(data, out) diff --git a/stub/stub.go b/stub/stub.go index ad1bb60c..66a292fa 100644 --- a/stub/stub.go +++ b/stub/stub.go @@ -3,6 +3,7 @@ package stub import ( "fmt" "log" + "net" "net/http" "os" @@ -25,7 +26,7 @@ func RunRestServer(ch chan struct{}, opt Options) { if opt.Port == "" { opt.Port = DefaultPort } - addr := opt.BindAddr + ":" + opt.Port + addr := net.JoinHostPort(opt.BindAddr, opt.Port) apiServer, _ := app.NewRestServer(opt.StubPath) From d65dfa82b2dd12fe624bec48a66673773ff293fa Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 17:01:12 +0300 Subject: [PATCH 07/16] tab --- protoc-gen-gripmock/server.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index 626a5aae..42f5c462 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -12,7 +12,7 @@ import ( "net" "net/http" - jsonpb "google.golang.org/protobuf/encoding/protojson" + jsonpb "google.golang.org/protobuf/encoding/protojson" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" From 8d204236ed9b264a4d929cc41c5e79584d7706be Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 19:44:17 +0300 Subject: [PATCH 08/16] algorithm stabilization --- pkg/storage/stubs.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/storage/stubs.go b/pkg/storage/stubs.go index 25322f91..ec47acc9 100644 --- a/pkg/storage/stubs.go +++ b/pkg/storage/stubs.go @@ -2,6 +2,7 @@ package storage import ( "errors" + "slices" "sync/atomic" "github.com/google/uuid" @@ -30,10 +31,6 @@ func (s *Stub) GetID() uuid.UUID { return *s.ID } -func (s *Stub) CheckHeaders() bool { - return (len(s.Headers.Equals) + len(s.Headers.Matches) + len(s.Headers.Contains)) > 0 -} - type Input struct { Equals map[string]interface{} `json:"equals"` Contains map[string]interface{} `json:"contains"` @@ -54,8 +51,12 @@ type storage struct { Output Output } +func (s *storage) CountHeaders() int { + return len(s.Headers.Equals) + len(s.Headers.Matches) + len(s.Headers.Contains) +} + func (s *storage) CheckHeaders() bool { - return (len(s.Headers.Equals) + len(s.Headers.Matches) + len(s.Headers.Contains)) > 0 + return s.CountHeaders() > 0 } type StubStorage struct { @@ -151,7 +152,6 @@ func (r *StubStorage) ItemsBy(service, method string, ID *uuid.UUID) ([]storage, return nil, err } - var resultWithHeaders []storage var result []storage for obj := it.Next(); obj != nil; obj = it.Next() { @@ -164,14 +164,14 @@ func (r *StubStorage) ItemsBy(service, method string, ID *uuid.UUID) ([]storage, Output: stub.Output, } - if stub.CheckHeaders() { - resultWithHeaders = append(resultWithHeaders, s) - } else { - result = append(result, s) - } + result = append(result, s) } - return append(resultWithHeaders, result...), nil + slices.SortFunc(result, func(a, b storage) int { + return b.CountHeaders() - a.CountHeaders() + }) + + return result, nil } func (r *StubStorage) Unused() []Stub { From 729d8f15c72adfe2d3df3d763594907c8f73c426 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 20:08:01 +0300 Subject: [PATCH 09/16] fix equals --- example/simple/stub/simple3.yaml | 2 +- protoc-gen-gripmock/server.tmpl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/example/simple/stub/simple3.yaml b/example/simple/stub/simple3.yaml index 57164105..884387be 100644 --- a/example/simple/stub/simple3.yaml +++ b/example/simple/stub/simple3.yaml @@ -10,7 +10,7 @@ - service: Gripmock method: SayHello headers: - contains: + equals: authorization: Basic dXNlcjp1c2Vy # user:user input: equals: diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index 42f5c462..97be6dbe 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -4,6 +4,7 @@ package main import ( "bytes" "errors" + "slices" "encoding/json" "fmt" "io" @@ -168,8 +169,13 @@ func findStub(ctx context.Context, service, method string, md metadata.MD, in, o return err } + excludes := []string{":authority", "content-type", "grpc-accept-encoding", "user-agent"} headers := make(map[string]string, len(md)) for h, v := range md { + if slices.Contains(excludes, h) { + continue + } + headers[h] = strings.Join(v, ";") } From f09db170469adb943e9c9eac84aa8565c29d0056 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 20:15:29 +0300 Subject: [PATCH 10/16] fix spaces --- protoc-gen-gripmock/server.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index 97be6dbe..70b839d2 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -169,12 +169,12 @@ func findStub(ctx context.Context, service, method string, md metadata.MD, in, o return err } - excludes := []string{":authority", "content-type", "grpc-accept-encoding", "user-agent"} + excludes := []string{":authority", "content-type", "grpc-accept-encoding", "user-agent"} headers := make(map[string]string, len(md)) for h, v := range md { - if slices.Contains(excludes, h) { - continue - } + if slices.Contains(excludes, h) { + continue + } headers[h] = strings.Join(v, ";") } From 780fa5e25f5a6f10a6ed56f180d12c5f146e8e44 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 20:18:09 +0300 Subject: [PATCH 11/16] lru update --- go.mod | 6 +++--- go.sum | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 944b683c..977015ca 100644 --- a/go.mod +++ b/go.mod @@ -24,8 +24,8 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/hashicorp/go-immutable-radix v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -34,7 +34,7 @@ require ( golang.org/x/net v0.15.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b5ac2d90..d7a1035d 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= @@ -42,6 +44,8 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -118,6 +122,8 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3j golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= From a72b26998cf73a0cd8d86d83863fff0c76985d4c Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 21:29:58 +0300 Subject: [PATCH 12/16] removed the extra module --- Dockerfile | 2 +- go.mod | 4 ---- protoc-gen-gripmock/go.mod | 6 +++--- protoc-gen-gripmock/go.sum | 12 ++++++------ protogen/empty.go | 10 ++++++++++ protogen/example/empty.go | 11 ----------- protogen/example/go.mod | 16 ---------------- protogen/example/go.sum | 21 --------------------- protogen/go.mod | 13 +++++++++++++ protogen/go.sum | 21 +++++++++++++++++++++ 10 files changed, 54 insertions(+), 62 deletions(-) delete mode 100644 protogen/example/empty.go delete mode 100644 protogen/example/go.mod delete mode 100644 protogen/example/go.sum diff --git a/Dockerfile b/Dockerfile index 8746d8f8..a4ac77f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ RUN mkdir /proto /stubs &&\ RUN cd /go/src/github.com/bavix/gripmock/protoc-gen-gripmock &&\ go install -v &&\ cd /go/src/github.com/bavix/gripmock/example/simple/client &&\ - go get -u all &&\ + go mod tidy &&\ cd /go/src/github.com/bavix/gripmock &&\ go install -v diff --git a/go.mod b/go.mod index 977015ca..1f53ce0f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.21 require ( github.com/bavix/gripmock/protogen v0.0.0 - github.com/bavix/gripmock/protogen/example v0.0.0 github.com/goccy/go-yaml v1.11.2 github.com/google/uuid v1.3.1 github.com/gorilla/handlers v1.5.1 @@ -39,8 +38,5 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -// this is for generated server to be able to run -replace github.com/bavix/gripmock/protogen/example v0.0.0 => ./protogen/example - // this is for example client to be able to run replace github.com/bavix/gripmock/protogen v0.0.0 => ./protogen diff --git a/protoc-gen-gripmock/go.mod b/protoc-gen-gripmock/go.mod index c9458eb3..f50dd3aa 100644 --- a/protoc-gen-gripmock/go.mod +++ b/protoc-gen-gripmock/go.mod @@ -3,13 +3,13 @@ module github.com/bavix/gripmock/protoc-gen-gripmock go 1.21 require ( - golang.org/x/text v0.12.0 - golang.org/x/tools v0.12.0 + golang.org/x/text v0.13.0 + golang.org/x/tools v0.13.0 google.golang.org/protobuf v1.31.0 ) require ( golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect ) diff --git a/protoc-gen-gripmock/go.sum b/protoc-gen-gripmock/go.sum index a9a6e455..185a88b7 100644 --- a/protoc-gen-gripmock/go.sum +++ b/protoc-gen-gripmock/go.sum @@ -3,12 +3,12 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= diff --git a/protogen/empty.go b/protogen/empty.go index 356b3890..c5273334 100644 --- a/protogen/empty.go +++ b/protogen/empty.go @@ -1 +1,11 @@ package protogen + +import ( + _ "google.golang.org/grpc" + _ "google.golang.org/grpc/codes" + _ "google.golang.org/grpc/status" + _ "google.golang.org/protobuf/reflect/protoreflect" + _ "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/known/apipb" + _ "google.golang.org/protobuf/types/known/emptypb" +) diff --git a/protogen/example/empty.go b/protogen/example/empty.go deleted file mode 100644 index 975dd7b4..00000000 --- a/protogen/example/empty.go +++ /dev/null @@ -1,11 +0,0 @@ -package example - -import ( - _ "google.golang.org/grpc" - _ "google.golang.org/grpc/codes" - _ "google.golang.org/grpc/status" - _ "google.golang.org/protobuf/reflect/protoreflect" - _ "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/known/apipb" - _ "google.golang.org/protobuf/types/known/emptypb" -) diff --git a/protogen/example/go.mod b/protogen/example/go.mod deleted file mode 100644 index 29e18fba..00000000 --- a/protogen/example/go.mod +++ /dev/null @@ -1,16 +0,0 @@ -module github.com/bavix/gripmock/protogen/example - -go 1.21 - -require ( - google.golang.org/grpc v1.57.0 - google.golang.org/protobuf v1.31.0 -) - -require ( - github.com/golang/protobuf v1.5.3 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect -) diff --git a/protogen/example/go.sum b/protogen/example/go.sum deleted file mode 100644 index ddcffb68..00000000 --- a/protogen/example/go.sum +++ /dev/null @@ -1,21 +0,0 @@ -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/protogen/go.mod b/protogen/go.mod index 7be0513c..c2e9d31b 100644 --- a/protogen/go.mod +++ b/protogen/go.mod @@ -1,3 +1,16 @@ module github.com/bavix/gripmock/protogen go 1.21 + +require ( + google.golang.org/grpc v1.58.2 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/golang/protobuf v1.5.3 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect +) diff --git a/protogen/go.sum b/protogen/go.sum index e69de29b..b5425ae0 100644 --- a/protogen/go.sum +++ b/protogen/go.sum @@ -0,0 +1,21 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= From 9847c8102a4ad3717600ba302f6c06c153e1d0eb Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sat, 30 Sep 2023 21:36:51 +0300 Subject: [PATCH 13/16] fix docs --- README.md | 2 +- docs/api-stubs-search.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index daa81b2e..eecb2f69 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ Stub will respond with the expected response only if the request matches any rul So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. ### Input Matching Rule -Input matching has 3 rules to match an input: **equals**,**contains** and **regex** +Input matching has 3 rules to match an input: **equals**,**contains** and **matches**
Nested fields are allowed for input matching too for all JSON data types. (`string`, `bool`, `array`, etc.)
diff --git a/docs/api-stubs-search.md b/docs/api-stubs-search.md index e666faab..f1f5ec0e 100644 --- a/docs/api-stubs-search.md +++ b/docs/api-stubs-search.md @@ -83,7 +83,7 @@ Stub will respond with the expected response only if the request matches any rul So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. ### Input Matching Rule -Input matching has 3 rules to match an input: **equals**,**contains** and **regex** +Input matching has 3 rules to match an input: **equals**,**contains** and **matches**
Nested fields are allowed for input matching too for all JSON data types. (`string`, `bool`, `array`, etc.)
From 017d5a22a628dc2b442dd7b9c9647f974830e7d3 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sun, 1 Oct 2023 18:24:22 +0300 Subject: [PATCH 14/16] Update docs --- README.md | 74 ++++++++++++++++++++++++++++++++++++++++ docs/api-stubs-search.md | 66 +++++++++++++++++++++++++++++++++++ docs/overview.md | 1 + docs/quick-usage.md | 5 +++ 4 files changed, 146 insertions(+) diff --git a/README.md b/README.md index eecb2f69..00b5cc0b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ This service is a fork of the service [tokopedia/gripmock](https://github.com/to - Updated all deprecated dependencies [tokopedia#64](https://github.com/tokopedia/gripmock/issues/64); - Add yaml as json alternative for static stab's; - Add endpoint for healthcheck (/api/health/liveness, /api/health/readiness); +- Add support headers [tokopedia#144](https://github.com/tokopedia/gripmock/issues/144); - Add grpc error code [tokopedia#125](https://github.com/tokopedia/gripmock/issues/125); - Added gzip encoding support for grpc server [tokopedia#134](https://github.com/tokopedia/gripmock/pull/134); - Fixed issues with int64/uint64 [tokopedia#67](https://github.com/tokopedia/gripmock/pull/148); @@ -74,6 +75,9 @@ Stub Format is JSON text format. It has a skeleton as follows: { "service":"", // name of service defined in proto "method":"", // name of method that we want to mock + "headers":{ // Optional. headers matching rule. see Headers Matching Rule section below + // put rule here + }, "input":{ // input matching rule. see Input Matching Rule section below // put rule here }, @@ -81,6 +85,9 @@ Stub Format is JSON text format. It has a skeleton as follows: "data":{ // put result fields here }, + "headers":{ // Optional + // put result headers here + }, "error":"", // Optional. if you want to return error instead. "code":"" // Optional. Grpc response code. if code !=0 return error instead. } @@ -191,6 +198,73 @@ Nested fields are allowed for input matching too for all JSON data types. (`stri } ``` +## Headers Matching +Stub will respond with the expected response only if the request matches any rule. Stub service will serve `/api/stubs/search` endpoint with format: +```json +{ + "service":"", + "method":"", + "data":{ + // input that suppose to match with stored stubs + } +} +``` +So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. + +### Headers Matching Rule +Headers matching has 3 rules to match an input: **equals**,**contains** and **matches** +
+Headers can consist of a key and a value. If there are several values, then you need to list them separated by ";". Data type string. +
+**Gripmock** recursively goes over the fields and tries to match with given input. +
+**equals** will match the exact field name and value of input into expected stub. example stub JSON: +```json +{ + . + . + "headers":{ + "equals":{ + "authorization": "mytoken", + "system": "ec071904-93bf-4ded-b49c-d06097ddc6d5" + } + } + . + . +} +``` + +**contains** will match input that has the value declared expected fields. example stub JSON: +```json +{ + . + . + "headers":{ + "contains":{ + "field2":"hello" + } + } + . + . +} +``` + +**matches** using regex for matching fields expectation. example: + +```json +{ + . + . + "headers":{ + "matches":{ + "name":"^grip.*$" + } + } + . + . +} +``` + --- Supported by diff --git a/docs/api-stubs-search.md b/docs/api-stubs-search.md index f1f5ec0e..0d1939da 100644 --- a/docs/api-stubs-search.md +++ b/docs/api-stubs-search.md @@ -147,3 +147,69 @@ Nested fields are allowed for input matching too for all JSON data types. (`stri } ``` +## Headers Matching +Stub will respond with the expected response only if the request matches any rule. Stub service will serve `/api/stubs/search` endpoint with format: +```json +{ + "service":"", + "method":"", + "data":{ + // input that suppose to match with stored stubs + } +} +``` +So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. + +### Headers Matching Rule +Headers matching has 3 rules to match an input: **equals**,**contains** and **matches** +
+Headers can consist of a key and a value. If there are several values, then you need to list them separated by ";". Data type string. +
+**Gripmock** recursively goes over the fields and tries to match with given input. +
+**equals** will match the exact field name and value of input into expected stub. example stub JSON: +```json +{ + . + . + "headers":{ + "equals":{ + "authorization": "mytoken", + "system": "ec071904-93bf-4ded-b49c-d06097ddc6d5" + } + } + . + . +} +``` + +**contains** will match input that has the value declared expected fields. example stub JSON: +```json +{ + . + . + "headers":{ + "contains":{ + "field2":"hello" + } + } + . + . +} +``` + +**matches** using regex for matching fields expectation. example: + +```json +{ + . + . + "headers":{ + "matches":{ + "name":"^grip.*$" + } + } + . + . +} +``` diff --git a/docs/overview.md b/docs/overview.md index a55ac71d..3b8e3235 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -10,6 +10,7 @@ This service is a fork of the service [tokopedia/gripmock](https://github.com/to - Updated all deprecated dependencies [tokopedia#64](https://github.com/tokopedia/gripmock/issues/64); - Add yaml as json alternative for static stab's; - Add endpoint for healthcheck (/api/health/liveness, /api/health/readiness); +- Add support headers [tokopedia#144](https://github.com/tokopedia/gripmock/issues/144); - Add grpc error code [tokopedia#125](https://github.com/tokopedia/gripmock/issues/125); - Added gzip encoding support for grpc server [tokopedia#134](https://github.com/tokopedia/gripmock/pull/134); - Fixed issues with int64/uint64 [tokopedia#67](https://github.com/tokopedia/gripmock/pull/148); diff --git a/docs/quick-usage.md b/docs/quick-usage.md index 7930b4e0..070449c6 100644 --- a/docs/quick-usage.md +++ b/docs/quick-usage.md @@ -63,6 +63,11 @@ The result will not make you wait long, you should see the following: "id": "6c85b0fa-caaf-4640-a672-f56b7dd8074d", "service": "Gripmock", "method": "SayHello", + "headers": { + "equals": null, + "contains": null, + "matches": null + }, "input": { "equals": { "name": "gripmock" From 2bec0659d91c323ab4591882c561873a72e7203e Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sun, 1 Oct 2023 18:34:13 +0300 Subject: [PATCH 15/16] matching rules --- docs/_sidebar.md | 4 + docs/api-stubs-search.md | 145 +--------------------------------- docs/matching-rule-headers.md | 66 ++++++++++++++++ docs/matching-rule-input.md | 77 ++++++++++++++++++ 4 files changed, 149 insertions(+), 143 deletions(-) create mode 100644 docs/matching-rule-headers.md create mode 100644 docs/matching-rule-input.md diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 13378217..2dfda79c 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -6,6 +6,10 @@ - [One service](proto-one-file) - [N-Services in one](proto-multifiles) +- Matching Rule + - [Input](matching-rule-input) + - [Headers](matching-rule-headers) + - Static stubs - [JSON](static-stubs-json) - [YAML](static-stubs-yaml) diff --git a/docs/api-stubs-search.md b/docs/api-stubs-search.md index 0d1939da..b2af2e80 100644 --- a/docs/api-stubs-search.md +++ b/docs/api-stubs-search.md @@ -69,147 +69,6 @@ Response: } ``` -## Input Matching -Stub will respond with the expected response only if the request matches any rule. Stub service will serve `/api/stubs/search` endpoint with format: -```json -{ - "service":"", - "method":"", - "data":{ - // input that suppose to match with stored stubs - } -} -``` -So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. - -### Input Matching Rule -Input matching has 3 rules to match an input: **equals**,**contains** and **matches** -
-Nested fields are allowed for input matching too for all JSON data types. (`string`, `bool`, `array`, etc.) -
-**Gripmock** recursively goes over the fields and tries to match with given input. -
-**equals** will match the exact field name and value of input into expected stub. example stub JSON: -```json -{ - . - . - "input":{ - "equals":{ - "name":"gripmock", - "greetings": { - "english": "Hello World!", - "indonesian": "Halo Dunia!", - "turkish": "Merhaba Dünya!" - }, - "ok": true, - "numbers": [4, 8, 15, 16, 23, 42] - "null": null - } - } - . - . -} -``` - -**contains** will match input that has the value declared expected fields. example stub JSON: -```json -{ - . - . - "input":{ - "contains":{ - "field2":"hello", - "field4":{ - "field5": "value5" - } - } - } - . - . -} -``` - -**matches** using regex for matching fields expectation. example: - -```json -{ - . - . - "input":{ - "matches":{ - "name":"^grip.*$", - "cities": ["Jakarta", "Istanbul", ".*grad$"] - } - } - . - . -} -``` +[Input Matching](matching-rule-input.md ':include') -## Headers Matching -Stub will respond with the expected response only if the request matches any rule. Stub service will serve `/api/stubs/search` endpoint with format: -```json -{ - "service":"", - "method":"", - "data":{ - // input that suppose to match with stored stubs - } -} -``` -So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. - -### Headers Matching Rule -Headers matching has 3 rules to match an input: **equals**,**contains** and **matches** -
-Headers can consist of a key and a value. If there are several values, then you need to list them separated by ";". Data type string. -
-**Gripmock** recursively goes over the fields and tries to match with given input. -
-**equals** will match the exact field name and value of input into expected stub. example stub JSON: -```json -{ - . - . - "headers":{ - "equals":{ - "authorization": "mytoken", - "system": "ec071904-93bf-4ded-b49c-d06097ddc6d5" - } - } - . - . -} -``` - -**contains** will match input that has the value declared expected fields. example stub JSON: -```json -{ - . - . - "headers":{ - "contains":{ - "field2":"hello" - } - } - . - . -} -``` - -**matches** using regex for matching fields expectation. example: - -```json -{ - . - . - "headers":{ - "matches":{ - "name":"^grip.*$" - } - } - . - . -} -``` +[Headers Matching](matching-rule-headers.md ':include') diff --git a/docs/matching-rule-headers.md b/docs/matching-rule-headers.md new file mode 100644 index 00000000..fe3dbe1c --- /dev/null +++ b/docs/matching-rule-headers.md @@ -0,0 +1,66 @@ +## Headers Matching +Stub will respond with the expected response only if the request matches any rule. Stub service will serve `/api/stubs/search` endpoint with format: +```json +{ + "service":"", + "method":"", + "data":{ + // input that suppose to match with stored stubs + } +} +``` +So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. + +### Headers Matching Rule +Headers matching has 3 rules to match an input: **equals**,**contains** and **matches** +
+Headers can consist of a key and a value. If there are several values, then you need to list them separated by ";". Data type string. +
+**Gripmock** recursively goes over the fields and tries to match with given input. +
+**equals** will match the exact field name and value of input into expected stub. example stub JSON: +```json +{ + . + . + "headers":{ + "equals":{ + "authorization": "mytoken", + "system": "ec071904-93bf-4ded-b49c-d06097ddc6d5" + } + } + . + . +} +``` + +**contains** will match input that has the value declared expected fields. example stub JSON: +```json +{ + . + . + "headers":{ + "contains":{ + "field2":"hello" + } + } + . + . +} +``` + +**matches** using regex for matching fields expectation. example: + +```json +{ + . + . + "headers":{ + "matches":{ + "name":"^grip.*$" + } + } + . + . +} +``` diff --git a/docs/matching-rule-input.md b/docs/matching-rule-input.md new file mode 100644 index 00000000..9e9d3946 --- /dev/null +++ b/docs/matching-rule-input.md @@ -0,0 +1,77 @@ +## Input Matching +Stub will respond with the expected response only if the request matches any rule. Stub service will serve `/api/stubs/search` endpoint with format: +```json +{ + "service":"", + "method":"", + "data":{ + // input that suppose to match with stored stubs + } +} +``` +So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/api/stubs/search` stub service will find a match from listed stubs stored there. + +### Input Matching Rule +Input matching has 3 rules to match an input: **equals**,**contains** and **matches** +
+Nested fields are allowed for input matching too for all JSON data types. (`string`, `bool`, `array`, etc.) +
+**Gripmock** recursively goes over the fields and tries to match with given input. +
+**equals** will match the exact field name and value of input into expected stub. example stub JSON: +```json +{ + . + . + "input":{ + "equals":{ + "name":"gripmock", + "greetings": { + "english": "Hello World!", + "indonesian": "Halo Dunia!", + "turkish": "Merhaba Dünya!" + }, + "ok": true, + "numbers": [4, 8, 15, 16, 23, 42] + "null": null + } + } + . + . +} +``` + +**contains** will match input that has the value declared expected fields. example stub JSON: +```json +{ + . + . + "input":{ + "contains":{ + "field2":"hello", + "field4":{ + "field5": "value5" + } + } + } + . + . +} +``` + +**matches** using regex for matching fields expectation. example: + +```json +{ + . + . + "input":{ + "matches":{ + "name":"^grip.*$", + "cities": ["Jakarta", "Istanbul", ".*grad$"] + } + } + . + . +} +``` From 620fff4511d023900b8ade40810f39400d2a1f4c Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sun, 1 Oct 2023 18:39:11 +0300 Subject: [PATCH 16/16] update version --- api/openapi/api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/openapi/api.yaml b/api/openapi/api.yaml index a12c9d8b..bd3861e7 100644 --- a/api/openapi/api.yaml +++ b/api/openapi/api.yaml @@ -2,7 +2,7 @@ openapi: 3.0.2 servers: - url: /api info: - version: 2.0.1 + version: 2.1.0 title: GripMock API Schema tags: - name: stubs