swagger updates, moved structs and types into types.go, and made helpers.go for custom handlers

This commit is contained in:
grumbulon 2023-02-01 19:41:38 -05:00
parent 9d87ae4728
commit 295360fd05
9 changed files with 137 additions and 127 deletions

View File

@ -50,13 +50,13 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
},
"401": {
"description": "authFailed is a 401 error when logging in fails, includes realm",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
}
}
@ -100,13 +100,13 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
},
"500": {
"description": "internalServerError is a 500 server error with a logged error call back",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
}
}
@ -150,13 +150,13 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
},
"500": {
"description": "internalServerError is a 500 server error with a logged error call back",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
}
}
@ -164,7 +164,15 @@ const docTemplate = `{
}
},
"definitions": {
"internal.GenericResponse-internal_Response": {
"internal.Response": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
}
},
"internal.SwaggerGenericResponse-internal_Response": {
"type": "object",
"properties": {
"response": {
@ -176,17 +184,6 @@ const docTemplate = `{
]
}
}
},
"internal.Response": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"status": {
"type": "integer"
}
}
}
},
"securityDefinitions": {

View File

@ -41,13 +41,13 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
},
"401": {
"description": "authFailed is a 401 error when logging in fails, includes realm",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
}
}
@ -91,13 +91,13 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
},
"500": {
"description": "internalServerError is a 500 server error with a logged error call back",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
}
}
@ -141,13 +141,13 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
},
"500": {
"description": "internalServerError is a 500 server error with a logged error call back",
"schema": {
"$ref": "#/definitions/internal.GenericResponse-internal_Response"
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
}
}
}
@ -155,7 +155,15 @@
}
},
"definitions": {
"internal.GenericResponse-internal_Response": {
"internal.Response": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
}
},
"internal.SwaggerGenericResponse-internal_Response": {
"type": "object",
"properties": {
"response": {
@ -167,17 +175,6 @@
]
}
}
},
"internal.Response": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"status": {
"type": "integer"
}
}
}
},
"securityDefinitions": {

View File

@ -1,18 +1,16 @@
definitions:
internal.GenericResponse-internal_Response:
internal.Response:
properties:
message:
type: string
type: object
internal.SwaggerGenericResponse-internal_Response:
properties:
response:
allOf:
- $ref: '#/definitions/internal.Response'
description: Response items
type: object
internal.Response:
properties:
message:
type: string
status:
type: integer
type: object
info:
contact: {}
description: Pomme is a service that parses zonefiles
@ -44,11 +42,11 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/internal.GenericResponse-internal_Response'
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
"401":
description: authFailed is a 401 error when logging in fails, includes realm
schema:
$ref: '#/definitions/internal.GenericResponse-internal_Response'
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
summary: authenticate as a regular user
tags:
- accounts
@ -77,12 +75,12 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/internal.GenericResponse-internal_Response'
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
"500":
description: internalServerError is a 500 server error with a logged error
call back
schema:
$ref: '#/definitions/internal.GenericResponse-internal_Response'
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
security:
- Bearer: []
summary: parse your zonefile
@ -113,12 +111,12 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/internal.GenericResponse-internal_Response'
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
"500":
description: internalServerError is a 500 server error with a logged error
call back
schema:
$ref: '#/definitions/internal.GenericResponse-internal_Response'
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
security:
- Bearer: []
summary: upload a zonefile

View File

@ -1,83 +1,16 @@
package api
import (
"context"
"fmt"
"log"
"net/http"
"time"
"git.freecumextremist.com/grumbulon/pomme/internal"
"git.freecumextremist.com/grumbulon/pomme/internal/db"
"github.com/go-chi/chi/v5"
"github.com/go-chi/httplog"
"github.com/go-chi/httprate"
"github.com/go-chi/jwtauth/v5"
"github.com/go-chi/render"
"gorm.io/gorm"
)
type key int
const (
keyPrincipalContextID key = iota
)
// setDBMiddleware is the http Handler func for the GORM middleware with context.
func setDBMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var pommeDB *gorm.DB
c, err := internal.ReadConfig()
if err != nil {
log.Printf("No config file defined: %v", err)
}
switch r.Header.Get("User-Agent") {
case "pomme-api-test-slave":
pommeDB = db.InitDb(c.TestDB)
default:
pommeDB = db.InitDb(c.DB)
}
timeoutContext, cancelContext := context.WithTimeout(context.Background(), time.Second)
ctx := context.WithValue(r.Context(), keyPrincipalContextID, pommeDB.WithContext(timeoutContext))
defer cancelContext()
next.ServeHTTP(w, r.WithContext(ctx))
})
}
type GenericResponse[T map[string]any] struct {
Response map[string]any `json:"response,omitempty"`
}
func APIError[T map[string]any](w http.ResponseWriter, r *http.Request, v map[string]any) {
logger := httplog.NewLogger("Pomme", httplog.Options{
JSON: true,
})
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
switch v["realm"] {
case nil:
w.Header().Add("API Error", v["message"].(string))
default:
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`realm="%s"`, v["realm"].(string)))
w.Header().Add("API Error", v["message"].(string))
}
w.WriteHeader(v["status"].(int))
render.JSON(w, r, v)
switch v["error"] {
case nil:
logger.Info().Msg(fmt.Sprint(v["message"]))
default:
logger.Error().Msg(fmt.Sprint(v["error"]))
}
}
// API subroute handler.
func API() http.Handler {
api := chi.NewRouter()

View File

@ -22,8 +22,8 @@ import (
// @Produce json
// @Param username query string true "Username"
// @Param password query string true "Password"
// @Success 200 {object} internal.GenericResponse[internal.Response]
// @failure 401 {object} internal.GenericResponse[internal.Response] "authFailed is a 401 error when logging in fails, includes realm"
// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response]
// @failure 401 {object} internal.SwaggerGenericResponse[internal.Response] "authFailed is a 401 error when logging in fails, includes realm"
// @Router /api/login [post]
func Login(w http.ResponseWriter, r *http.Request) {
var result internal.User

75
internal/api/helpers.go Normal file
View File

@ -0,0 +1,75 @@
package api
import (
"context"
"fmt"
"log"
"net/http"
"time"
"git.freecumextremist.com/grumbulon/pomme/internal"
"git.freecumextremist.com/grumbulon/pomme/internal/db"
"github.com/go-chi/httplog"
"github.com/go-chi/render"
"gorm.io/gorm"
)
// setDBMiddleware is the http Handler func for the GORM middleware with context.
func setDBMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var pommeDB *gorm.DB
c, err := internal.ReadConfig()
if err != nil {
log.Printf("No config file defined: %v", err)
}
switch r.Header.Get("User-Agent") {
case "pomme-api-test-slave":
pommeDB = db.InitDb(c.TestDB)
default:
pommeDB = db.InitDb(c.DB)
}
timeoutContext, cancelContext := context.WithTimeout(context.Background(), time.Second)
ctx := context.WithValue(r.Context(), keyPrincipalContextID, pommeDB.WithContext(timeoutContext))
defer cancelContext()
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func APIError[T map[string]any](w http.ResponseWriter, r *http.Request, v map[string]any) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
logHandler(v)
switch v["realm"] {
case nil:
w.Header().Add("API Error", v["message"].(string))
default:
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`realm="%s"`, v["realm"].(string)))
w.Header().Add("API Error", v["message"].(string))
}
w.WriteHeader(v["status"].(int))
delete(v, "error")
render.JSON(w, r, v)
}
func logHandler(v map[string]any) {
logger := httplog.NewLogger("Pomme", httplog.Options{
JSON: true,
})
switch v["error"] {
default:
logger.Error().Msg(v["error"].(string))
fallthrough
case nil:
logger.Info().Msg(v["message"].(string))
}
}

View File

@ -2,6 +2,14 @@ package api
import "gorm.io/gorm"
const (
keyPrincipalContextID key = iota
)
type genericResponseFields map[string]any
type key int
// ZoneRequest represents a Zone file request.
type ZoneRequest struct {
*Zone
@ -17,4 +25,6 @@ type Zone struct {
Body string `json:"body,omitempty"`
}
type genericResponseFields map[string]any
type GenericResponse[T map[string]any] struct {
Response map[string]any `json:"response,omitempty"`
}

View File

@ -29,8 +29,8 @@ import (
// @Accept mpfd
// @Produce json
// @Param file formData file true "Zonefile to upload"
// @Success 200 {object} internal.GenericResponse[internal.Response]
// @Failure 500 {object} internal.GenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back"
// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response]
// @Failure 500 {object} internal.SwaggerGenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back"
// @Param Authorization header string true "Bearer Token"
//
// @Security Bearer
@ -114,8 +114,8 @@ func ReceiveFile(w http.ResponseWriter, r *http.Request) {
// @Accept mpfd
// @Produce json
// @Param filename query string true "Zonefile name"
// @Success 200 {object} internal.GenericResponse[internal.Response]
// @Failure 500 {object} internal.GenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back"
// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response]
// @Failure 500 {object} internal.SwaggerGenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back"
// @Param Authorization header string true "Bearer Token"
//
// @Security Bearer

View File

@ -37,9 +37,9 @@ type Config struct {
TestDB string
}
// GenericNestedResponse[T]
// @Description Some Generic List Response.
type GenericResponse[T any] struct {
// SwaggerGenericResponse[T]
// @Description Generic Response for Swagger.
type SwaggerGenericResponse[T any] struct {
// Response items
Response T
}