Skip to content
Snippets Groups Projects
Commit bbe493a2 authored by OZGCloud's avatar OZGCloud
Browse files

OZG-6730 Mock Endpunkte

parent 0b282fbd
Branches
Tags
No related merge requests found
...@@ -6,13 +6,12 @@ Bibliothek [gRPC-Gateway](https://grpc-ecosystem.github.io/grpc-gateway/) verwen ...@@ -6,13 +6,12 @@ Bibliothek [gRPC-Gateway](https://grpc-ecosystem.github.io/grpc-gateway/) verwen
können die HTTP-Endpunkte inkl. des Mappings größtenteils automatisch aus proto-Dateien können die HTTP-Endpunkte inkl. des Mappings größtenteils automatisch aus proto-Dateien
generiert werden. generiert werden.
Requests werden zunächst an gRPC-Router weitergeleitet. Das sind Proxy-eigene gRPC-Server, die Requests werden zunächst an den gRPC-Router weitergeleitet. Das ist ein Proxy-eigener gRPC-Server, der
auf localhost und den unter config.grpc.router eingetragenen Ports laufen. Dort wird die auf localhost und dem unter config.grpc.router.port eingetragenen Port läuft. Dort wird die
Adresse des Ziel-Vorgang-Managers aus den HTTP Headers extrahiert. Der Port des Adresse des Ziel-Vorgang-Managers aus einem HTTP Header extrahiert. Der Port des
Ziel-Vorgang-Managers ist konstant und wird unter config.grpc.server.port festgelegt Ziel-Vorgang-Managers ist konstant und wird unter config.grpc.server.port festgelegt. Anschließend
(bzw. auf 50052 gesetzt, wenn config.grpc.mock = true). Anschließend leitet der VorgangRouter leitet der gRPC-Router die gRPC Request an die URL weiter, die aus der extrahierten Adresse und dem
die gRPC Request an die URL weiter, die aus der extrahierten Adresse und dem Port zusammengesetzt Port zusammengesetzt wird.
wird.
## Getting Started ## Getting Started
...@@ -59,12 +58,7 @@ grpc: ...@@ -59,12 +58,7 @@ grpc:
mock: gRPC Server mocken (bool) mock: gRPC Server mocken (bool)
port: Port des gRPC Servers (int) port: Port des gRPC Servers (int)
router: router:
rueckfragen: port: Port des gRPC Routers (int)
port: Port des gRPC Rueckfrage Routers (int)
commands:
port: Port des gRPC Command Routers (int)
binaryfiles:
port: Port des gRPC Binaryfile Routers (int)
logging: logging:
level: "ERROR" | "WARN" | "INFO" | "DEBUG" level: "ERROR" | "WARN" | "INFO" | "DEBUG"
``` ```
\ No newline at end of file
...@@ -32,14 +32,18 @@ import ( ...@@ -32,14 +32,18 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"io"
"net" "net"
"os"
) )
type server struct { const dummyFilePath = "internal/mock/testdata/dummy.pdf"
type rueckfrageServer struct {
pb.UnimplementedAntragraumServiceServer pb.UnimplementedAntragraumServiceServer
} }
func (s *server) FindRueckfragen(ctx context.Context, in *pb.GrpcFindRueckfragenRequest) (*pb.GrpcFindRueckfragenResponse, error) { func (s *rueckfrageServer) FindRueckfragen(_ context.Context, in *pb.GrpcFindRueckfragenRequest) (*pb.GrpcFindRueckfragenResponse, error) {
if in.PostfachId == "" { if in.PostfachId == "" {
return nil, status.Error(codes.InvalidArgument, "Id is missing") return nil, status.Error(codes.InvalidArgument, "Id is missing")
} }
...@@ -55,9 +59,84 @@ func (s *server) FindRueckfragen(ctx context.Context, in *pb.GrpcFindRueckfragen ...@@ -55,9 +59,84 @@ func (s *server) FindRueckfragen(ctx context.Context, in *pb.GrpcFindRueckfragen
return &pb.GrpcFindRueckfragenResponse{}, nil return &pb.GrpcFindRueckfragenResponse{}, nil
} }
func (s *rueckfrageServer) GetRueckfrage(_ context.Context, in *pb.GrpcGetRueckfrageRequest) (*pb.GrpcGetRueckfrageResponse, error) {
if in.Id == "" {
return nil, status.Error(codes.InvalidArgument, "Id is missing")
}
return &pb.GrpcGetRueckfrageResponse{}, nil
}
func (s *rueckfrageServer) SendRueckfrageAnswer(_ context.Context, _ *pb.GrpcSendRueckfrageAnswerRequest) (*pb.GrpcSendRueckfrageAnswerResponse, error) {
return &pb.GrpcSendRueckfrageAnswerResponse{}, nil
}
type commandServer struct {
pb.UnimplementedCommandServiceServer
}
func (s *commandServer) GetCommand(_ context.Context, in *pb.GrpcGetCommandRequest) (*pb.GrpcCommand, error) {
if in.Id == "" {
return nil, status.Error(codes.InvalidArgument, "Id is missing")
}
return &pb.GrpcCommand{}, nil
}
type binaryFileServer struct {
pb.UnimplementedBinaryFileServiceServer
}
func (s *binaryFileServer) FindBinaryFilesMetaData(_ context.Context, _ *pb.GrpcBinaryFilesRequest) (*pb.GrpcFindFilesResponse, error) {
return &pb.GrpcFindFilesResponse{}, nil
}
func (s *binaryFileServer) GetBinaryFileContent(_ *pb.GrpcGetBinaryFileDataRequest, stream pb.BinaryFileService_GetBinaryFileContentServer) error {
file, err := os.Open(dummyFilePath)
if err != nil {
return err
}
defer file.Close()
buffer := make([]byte, 1024*1024)
for {
bytesRead, err := file.Read(buffer)
if err == io.EOF {
break
}
if err != nil {
return err
}
chunk := &pb.GrpcGetBinaryFileDataResponse{
FileContent: buffer[:bytesRead],
}
if err := stream.Send(chunk); err != nil {
return err
}
}
return nil
}
func (s *binaryFileServer) UploadBinaryFileAsStream(stream pb.BinaryFileService_UploadBinaryFileAsStreamServer) error {
for {
_, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.GrpcUploadBinaryFileResponse{FileId: "testFileId"})
}
if err != nil {
return err
}
}
}
func StartGrpcServer() *grpc.Server { func StartGrpcServer() *grpc.Server {
s := grpc.NewServer() s := grpc.NewServer()
pb.RegisterAntragraumServiceServer(s, &server{}) pb.RegisterAntragraumServiceServer(s, &rueckfrageServer{})
pb.RegisterCommandServiceServer(s, &commandServer{})
pb.RegisterBinaryFileServiceServer(s, &binaryFileServer{})
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.Grpc.Server.Port)) lis, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.Grpc.Server.Port))
if err != nil { if err != nil {
......
File added
...@@ -55,6 +55,30 @@ func RegisterHomeEndpoint(mux *runtime.ServeMux) { ...@@ -55,6 +55,30 @@ func RegisterHomeEndpoint(mux *runtime.ServeMux) {
} }
} }
func RegisterAntragraumEndpoints(ctx context.Context, mux *runtime.ServeMux, grpcUrl string) {
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := pb.RegisterAntragraumServiceHandlerFromEndpoint(ctx, mux, grpcUrl, opts)
if err != nil {
logger.Fatal("failed to register antragraum endpoints: %v", err)
}
}
func RegisterCommandEndpoints(ctx context.Context, mux *runtime.ServeMux, grpcUrl string) {
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := pb.RegisterCommandServiceHandlerFromEndpoint(ctx, mux, grpcUrl, opts)
if err != nil {
logger.Fatal("failed to register command endpoints: %v", err)
}
}
func RegisterBinaryFileEndpoints(ctx context.Context, mux *runtime.ServeMux, grpcUrl string) {
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := pb.RegisterBinaryFileServiceHandlerFromEndpoint(ctx, mux, grpcUrl, opts)
if err != nil {
logger.Fatal("failed to register binary file endpoints: %v", err)
}
}
func RegisterFileUploadEndpoint(mux *runtime.ServeMux) { func RegisterFileUploadEndpoint(mux *runtime.ServeMux) {
err := mux.HandlePath("POST", "/api/v1/file", func(w http.ResponseWriter, r *http.Request, _ map[string]string) { err := mux.HandlePath("POST", "/api/v1/file", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
if err := r.ParseMultipartForm(32 << 20); err != nil { if err := r.ParseMultipartForm(32 << 20); err != nil {
...@@ -91,7 +115,7 @@ func RegisterFileUploadEndpoint(mux *runtime.ServeMux) { ...@@ -91,7 +115,7 @@ func RegisterFileUploadEndpoint(mux *runtime.ServeMux) {
}, },
} }
if err := stream.Send(meta); err != nil { if err := stream.Send(meta); err != nil {
http.Error(w, "Metadata send error", http.StatusInternalServerError) http.Error(w, "metadata send error", http.StatusInternalServerError)
return return
} }
...@@ -102,20 +126,20 @@ func RegisterFileUploadEndpoint(mux *runtime.ServeMux) { ...@@ -102,20 +126,20 @@ func RegisterFileUploadEndpoint(mux *runtime.ServeMux) {
break break
} }
if err != nil { if err != nil {
http.Error(w, "File read error", http.StatusInternalServerError) http.Error(w, "file read error", http.StatusInternalServerError)
return return
} }
if err := stream.Send(&pb.GrpcUploadBinaryFileRequest{
Request: &pb.GrpcUploadBinaryFileRequest_FileContent{FileContent: buf[:n]}, req := &pb.GrpcUploadBinaryFileRequest{Request: &pb.GrpcUploadBinaryFileRequest_FileContent{FileContent: buf[:n]}}
}); err != nil { if err = stream.Send(req); err != nil {
http.Error(w, "Chunk send error", http.StatusInternalServerError) http.Error(w, "chunk send error", http.StatusInternalServerError)
return return
} }
} }
resp, err := stream.CloseAndRecv() resp, err := stream.CloseAndRecv()
if err != nil { if err != nil {
http.Error(w, "Response error", http.StatusInternalServerError) http.Error(w, "response error", http.StatusInternalServerError)
return return
} }
...@@ -124,12 +148,12 @@ func RegisterFileUploadEndpoint(mux *runtime.ServeMux) { ...@@ -124,12 +148,12 @@ func RegisterFileUploadEndpoint(mux *runtime.ServeMux) {
}) })
if err != nil { if err != nil {
logger.Fatal("Endpoint registration failed: %v", err) logger.Fatal("endpoint registration failed: %v", err)
} }
} }
func RegisterGetFileEndpoint(mux *runtime.ServeMux) { func RegisterGetFileEndpoint(mux *runtime.ServeMux) {
err := mux.HandlePath("POST", "/api/v1/file/content/{fileId}", func(w http.ResponseWriter, r *http.Request, vars map[string]string) { err := mux.HandlePath("GET", "/api/v1/file/content/{fileId}", func(w http.ResponseWriter, r *http.Request, vars map[string]string) {
var fileBuffer bytes.Buffer var fileBuffer bytes.Buffer
conn, err := grpc.NewClient(getGrpcServerUrl(r.Header.Get(GrpcAddressHeader), conf), grpc.WithTransportCredentials(insecure.NewCredentials())) conn, err := grpc.NewClient(getGrpcServerUrl(r.Header.Get(GrpcAddressHeader), conf), grpc.WithTransportCredentials(insecure.NewCredentials()))
...@@ -153,13 +177,13 @@ func RegisterGetFileEndpoint(mux *runtime.ServeMux) { ...@@ -153,13 +177,13 @@ func RegisterGetFileEndpoint(mux *runtime.ServeMux) {
break break
} }
if err != nil { if err != nil {
http.Error(w, "Error receiving file chunks", http.StatusInternalServerError) http.Error(w, "error receiving file chunks", http.StatusInternalServerError)
return return
} }
_, err = fileBuffer.Write(resp.FileContent) _, err = fileBuffer.Write(resp.FileContent)
if err != nil { if err != nil {
http.Error(w, "Error writing file data", http.StatusInternalServerError) http.Error(w, "error writing file data", http.StatusInternalServerError)
return return
} }
} }
...@@ -171,25 +195,25 @@ func RegisterGetFileEndpoint(mux *runtime.ServeMux) { ...@@ -171,25 +195,25 @@ func RegisterGetFileEndpoint(mux *runtime.ServeMux) {
mw := multipart.NewWriter(w) mw := multipart.NewWriter(w)
part, err := mw.CreateFormFile("file", "file") part, err := mw.CreateFormFile("file", "file")
if err != nil { if err != nil {
http.Error(w, "Error creating multipart form file", http.StatusInternalServerError) http.Error(w, "error creating multipart form file", http.StatusInternalServerError)
return return
} }
_, err = fileBuffer.WriteTo(part) _, err = fileBuffer.WriteTo(part)
if err != nil { if err != nil {
http.Error(w, "Error writing file data to multipart", http.StatusInternalServerError) http.Error(w, "error writing file data to multipart", http.StatusInternalServerError)
return return
} }
err = mw.Close() err = mw.Close()
if err != nil { if err != nil {
http.Error(w, "Error closing multipart writer", http.StatusInternalServerError) http.Error(w, "error closing multipart writer", http.StatusInternalServerError)
return return
} }
}) })
if err != nil { if err != nil {
logger.Fatal("Endpoint registration failed: %v", err) logger.Fatal("endpoint registration failed: %v", err)
} }
} }
......
/*
* Copyright (C) 2023-2024
* Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package server
import (
"antragsraum-proxy/internal/mock"
"bytes"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)
func TestRegisterHomeEndpoint(t *testing.T) {
SetUpHttpGateway()
resp, err := http.Get("http://localhost:8082/")
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRegisterAntragraumEndpoints(t *testing.T) {
mock.SetUpGrpcServer()
SetUpHttpGateway()
jsonData := []byte(`{"fachstelle": {"mukId": "testMukId"}}`)
resp, err := http.Post("http://localhost:8080/api/fachstellen", "application/json", bytes.NewBuffer(jsonData))
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestErrorHandler(t *testing.T) {
assertErroneousRequest := func(jsonData []byte, expectedStatusCode int) {
mock.SetUpGrpcServer()
SetUpHttpGateway()
resp, err := http.Post("http://localhost:8080/api/fachstellen", "application/json", bytes.NewBuffer(jsonData))
assert.NoError(t, err)
assert.Equal(t, expectedStatusCode, resp.StatusCode)
}
t.Run("should have bad request error", func(t *testing.T) {
jsonData := []byte(`{}`)
assertErroneousRequest(jsonData, http.StatusBadRequest)
})
t.Run("should have conflict error", func(t *testing.T) {
jsonData := []byte(`{"fachstelle": {"mukId": "conflictMukId"}}`)
assertErroneousRequest(jsonData, http.StatusConflict)
})
t.Run("should have unavailable error", func(t *testing.T) {
jsonData := []byte(`{"fachstelle": {"mukId": "unavailableMukId"}}`)
assertErroneousRequest(jsonData, http.StatusServiceUnavailable)
})
t.Run("should have internal server error", func(t *testing.T) {
jsonData := []byte(`{"fachstelle": {"mukId": "erroneousMukId"}}`)
assertErroneousRequest(jsonData, http.StatusInternalServerError)
})
}
...@@ -28,14 +28,10 @@ package server ...@@ -28,14 +28,10 @@ package server
import ( import (
"context" "context"
"fmt" "fmt"
"google.golang.org/grpc/credentials/insecure"
"net/http" "net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
pb "antragsraum-proxy/gen/go"
) )
func HeaderMatcher(header string) (string, bool) { func HeaderMatcher(header string) (string, bool) {
...@@ -53,27 +49,12 @@ func StartHttpGateway() *http.Server { ...@@ -53,27 +49,12 @@ func StartHttpGateway() *http.Server {
defer cancel() defer cancel()
mux := runtime.NewServeMux(runtime.WithErrorHandler(ErrorHandler), runtime.WithIncomingHeaderMatcher(HeaderMatcher)) mux := runtime.NewServeMux(runtime.WithErrorHandler(ErrorHandler), runtime.WithIncomingHeaderMatcher(HeaderMatcher))
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} grpcRouterUrl := fmt.Sprintf("localhost:%d", conf.Grpc.Router.Port)
rueckfrageRouterUrl := fmt.Sprintf("localhost:%d", conf.Grpc.Router.Port)
err := pb.RegisterAntragraumServiceHandlerFromEndpoint(ctx, mux, rueckfrageRouterUrl, opts)
if err != nil {
logger.Fatal("failed to start HTTP gateway: %v", err)
}
commandRouterUrl := fmt.Sprintf("localhost:%d", conf.Grpc.Router.Port)
err = pb.RegisterCommandServiceHandlerFromEndpoint(ctx, mux, commandRouterUrl, opts)
if err != nil {
logger.Fatal("failed to start HTTP gateway: %v", err)
}
binaryFileRouterUrl := fmt.Sprintf("localhost:%d", conf.Grpc.Router.Port)
err = pb.RegisterBinaryFileServiceHandlerFromEndpoint(ctx, mux, binaryFileRouterUrl, opts)
if err != nil {
logger.Fatal("failed to start HTTP gateway: %v", err)
}
RegisterHomeEndpoint(mux) RegisterHomeEndpoint(mux)
RegisterAntragraumEndpoints(ctx, mux, grpcRouterUrl)
RegisterCommandEndpoints(ctx, mux, grpcRouterUrl)
RegisterBinaryFileEndpoints(ctx, mux, grpcRouterUrl)
RegisterFileUploadEndpoint(mux) RegisterFileUploadEndpoint(mux)
RegisterGetFileEndpoint(mux) RegisterGetFileEndpoint(mux)
...@@ -83,7 +64,7 @@ func StartHttpGateway() *http.Server { ...@@ -83,7 +64,7 @@ func StartHttpGateway() *http.Server {
} }
logger.Info("HTTP gateway listening on port %d", conf.Http.Server.Port) logger.Info("HTTP gateway listening on port %d", conf.Http.Server.Port)
if err = httpServer.ListenAndServe(); err != nil { if err := httpServer.ListenAndServe(); err != nil {
logger.Fatal("HTTP gateway failed to serve: %v", err) logger.Fatal("HTTP gateway failed to serve: %v", err)
} }
......
...@@ -32,6 +32,20 @@ import ( ...@@ -32,6 +32,20 @@ import (
"time" "time"
) )
func TestHeaderMatcher(t *testing.T) {
t.Run("should accept header", func(t *testing.T) {
_, accepted := HeaderMatcher(GrpcAddressHeader)
assert.True(t, accepted)
})
t.Run("should reject header", func(t *testing.T) {
_, accepted := HeaderMatcher("X-Rejected-Header")
assert.False(t, accepted)
})
}
func TestStartHttpGateway(t *testing.T) { func TestStartHttpGateway(t *testing.T) {
SetUpHttpGateway() SetUpHttpGateway()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment