Merged in feature/docresult (pull request #105)
Document Result Endpoint * testing * cleantesting * progress * query * lint * readme * dockerclient * api * passtest * tests * test
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"queryorchestration/internal/server"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/labstack/echo-contrib/echoprometheus"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
_ "github.com/lib/pq"
|
||||
echoSwagger "github.com/swaggo/echo-swagger"
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
server.Config
|
||||
SetRouter(*echo.Echo)
|
||||
GetRouter() *echo.Echo
|
||||
RegisterHandlers() (*openapi3.T, error)
|
||||
}
|
||||
|
||||
type BaseConfig struct {
|
||||
server.BaseConfig
|
||||
RegisterHandlersFunc func() (*openapi3.T, error)
|
||||
OpenAPI *openapi3.T
|
||||
Router *echo.Echo
|
||||
}
|
||||
|
||||
func (c *BaseConfig) SetRouter(e *echo.Echo) {
|
||||
c.Router = e
|
||||
}
|
||||
func (c *BaseConfig) GetRouter() *echo.Echo {
|
||||
return c.Router
|
||||
}
|
||||
func (c *BaseConfig) RegisterHandlers() (*openapi3.T, error) {
|
||||
if c.RegisterHandlersFunc == nil {
|
||||
return nil, errors.New("registerHandlersFunc required")
|
||||
}
|
||||
|
||||
o, err := c.RegisterHandlersFunc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.OpenAPI = o
|
||||
return c.OpenAPI, nil
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
port int
|
||||
host string
|
||||
address string
|
||||
router *echo.Echo
|
||||
cleanup func() error
|
||||
}
|
||||
|
||||
func getSlogCustomEchoMiddleware(logger *slog.Logger) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
start := time.Now()
|
||||
|
||||
err := next(c)
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
|
||||
logger.Info("HTTP request",
|
||||
"method", req.Method,
|
||||
"uri", req.RequestURI,
|
||||
"status", res.Status,
|
||||
"latency", time.Since(start).String(),
|
||||
"error", err,
|
||||
"remote_ip", c.RealIP(),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cfg Config) (*Server, error) {
|
||||
cleanup, err := server.New(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e := echo.New()
|
||||
|
||||
// Add Prometheus middleware
|
||||
e.Use(echoprometheus.NewMiddleware("echo")) // Add the middleware
|
||||
e.GET("/metrics", echoprometheus.NewHandler())
|
||||
|
||||
// Other middlewares
|
||||
e.Use(getSlogCustomEchoMiddleware(cfg.GetLogger()))
|
||||
e.Use(middleware.Recover())
|
||||
e.Use(middleware.CORS())
|
||||
|
||||
cfg.SetRouter(e)
|
||||
|
||||
opnapi, err := cfg.RegisterHandlers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonFunc, err := getSwaggerJSONFunc(opnapi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.GET("/swagger/doc.json", jsonFunc)
|
||||
|
||||
yamlFunc, err := getSwaggerYAMLFunc(opnapi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.GET("/swagger/doc.yaml", yamlFunc)
|
||||
|
||||
e.GET("/swagger/*", echoSwagger.EchoWrapHandler(
|
||||
echoSwagger.DocExpansion("full"),
|
||||
echoSwagger.DeepLinking(true),
|
||||
echoSwagger.DomID("swagger-ui"),
|
||||
echoSwagger.PersistAuthorization(true),
|
||||
))
|
||||
|
||||
host := "0.0.0.0"
|
||||
port := 8080
|
||||
address := net.JoinHostPort(host, strconv.Itoa(port))
|
||||
|
||||
return &Server{
|
||||
port: port,
|
||||
host: host,
|
||||
address: address,
|
||||
router: e,
|
||||
cleanup: cleanup,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Listen() {
|
||||
slog.Info("Listening for requests", "port", s.port)
|
||||
defer func() {
|
||||
if err := s.cleanup(); err != nil {
|
||||
// Log cleanup error but don't panic since we're shutting down
|
||||
s.router.Logger.Errorf("Error during cleanup: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
err := s.router.Start(s.address)
|
||||
if err != nil {
|
||||
s.router.Logger.Panic(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
|
||||
"queryorchestration/internal/serviceconfig/logger"
|
||||
"queryorchestration/internal/test"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping long test in short mode")
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
cfg := &BaseConfig{}
|
||||
test.SetCfgProvider(t, cfg)
|
||||
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
|
||||
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
|
||||
Cfg: cfg,
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
registerHandlers := func() (*openapi3.T, error) {
|
||||
return nil, nil
|
||||
}
|
||||
cfg.RegisterHandlersFunc = registerHandlers
|
||||
|
||||
serverInstance, err := New(ctx, cfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 8080, serverInstance.port)
|
||||
assert.Equal(t, "0.0.0.0", serverInstance.host)
|
||||
assert.Equal(t, "0.0.0.0:8080", serverInstance.address)
|
||||
}
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
server := Server{}
|
||||
|
||||
assert.Panics(t, func() { server.Listen() })
|
||||
}
|
||||
|
||||
func TestRegisterHandlers(t *testing.T) {
|
||||
c := BaseConfig{}
|
||||
_, err := c.RegisterHandlers()
|
||||
assert.Error(t, err)
|
||||
|
||||
c.RegisterHandlersFunc = func() (*openapi3.T, error) {
|
||||
return &openapi3.T{}, nil
|
||||
}
|
||||
op, err := c.RegisterHandlers()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &openapi3.T{}, op)
|
||||
assert.Equal(t, op, c.OpenAPI)
|
||||
}
|
||||
|
||||
func TestSetRouter(t *testing.T) {
|
||||
c := BaseConfig{}
|
||||
assert.Nil(t, c.Router)
|
||||
c.SetRouter(&echo.Echo{})
|
||||
assert.Equal(t, &echo.Echo{}, c.Router)
|
||||
}
|
||||
|
||||
func TestGetRouter(t *testing.T) {
|
||||
c := BaseConfig{}
|
||||
r := c.GetRouter()
|
||||
assert.Nil(t, r)
|
||||
assert.Equal(t, r, c.Router)
|
||||
|
||||
c.Router = &echo.Echo{}
|
||||
r = c.GetRouter()
|
||||
assert.Equal(t, &echo.Echo{}, r)
|
||||
assert.Equal(t, r, c.Router)
|
||||
}
|
||||
|
||||
func TestGetSlogCustomEchoMiddleware(t *testing.T) {
|
||||
l := &logger.TestLogger{
|
||||
T: t,
|
||||
}
|
||||
sl := slog.New(l)
|
||||
handlerFunc := getSlogCustomEchoMiddleware(sl)
|
||||
require.NotNil(t, handlerFunc)
|
||||
|
||||
mid := func(c echo.Context) error {
|
||||
sl.Info("in middleware", "location", "middleware")
|
||||
return nil
|
||||
}
|
||||
myFunc := handlerFunc(mid)
|
||||
require.NotNil(t, myFunc)
|
||||
|
||||
uri := "/example"
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, uri, strings.NewReader("hello"))
|
||||
rec := httptest.NewRecorder()
|
||||
ctx := echo.New().NewContext(req, rec)
|
||||
err := myFunc(ctx)
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.Len(t, l.Logs, 2)
|
||||
assert.Equal(t, "middleware", l.Logs[0]["location"])
|
||||
assert.Equal(t, uri, l.Logs[1]["uri"])
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func getSwaggerJSONFunc(opnapi *openapi3.T) (func(echo.Context) error, error) {
|
||||
spec, err := opnapi.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(c echo.Context) error {
|
||||
return c.Blob(http.StatusOK, "application/json", spec)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getSwaggerYAMLFunc(opnapi *openapi3.T) (func(echo.Context) error, error) {
|
||||
spec, err := opnapi.MarshalYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yaml, err := yaml.Marshal(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(c echo.Context) error {
|
||||
return c.Blob(http.StatusOK, "application/yaml", yaml)
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
queryapi "queryorchestration/api/queryAPI"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetSwaggerJSONFunc(t *testing.T) {
|
||||
openapi, err := queryapi.GetSwagger()
|
||||
require.NoError(t, err)
|
||||
|
||||
f, err := getSwaggerJSONFunc(openapi)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, f)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("hello"))
|
||||
rec := httptest.NewRecorder()
|
||||
ctx := echo.New().NewContext(req, rec)
|
||||
|
||||
err = f(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Result().StatusCode)
|
||||
}
|
||||
|
||||
func TestGetSwaggerYAMLFunc(t *testing.T) {
|
||||
openapi, err := queryapi.GetSwagger()
|
||||
require.NoError(t, err)
|
||||
|
||||
f, err := getSwaggerYAMLFunc(openapi)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, f)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("hello"))
|
||||
rec := httptest.NewRecorder()
|
||||
ctx := echo.New().NewContext(req, rec)
|
||||
|
||||
err = f(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Result().StatusCode)
|
||||
}
|
||||
Reference in New Issue
Block a user