-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
168bcda
commit ec64176
Showing
9 changed files
with
419 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Created by: haimkastner | ||
# Created at: 2025-02-06 21:23:13 UTC | ||
|
||
name: CI | ||
|
||
on: | ||
push | ||
|
||
jobs: | ||
test: | ||
name: Test | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: '1.21' | ||
cache: true | ||
|
||
- name: Install dependencies | ||
run: | | ||
go mod download | ||
- name: Run tests | ||
run: go test ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"cSpell.words": [ | ||
"Gleece" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,15 @@ | ||
# gleece-runtime | ||
# Gleece Runtime | ||
|
||
Core runtime package for the [Gleece](https://github.com/gopher-fleece/gleece) tool. | ||
|
||
## Installation | ||
|
||
```bash | ||
go get github.com/gopher-fleece/gleece-runtime | ||
``` | ||
|
||
This package is required dependency for projects using Gleece-generated routes for routing. | ||
|
||
The package contains all runtime dependencies needed by the generated code and controllers. | ||
|
||
To generate routes & specs, use the [Gleece](https://github.com/gopher-fleece/gleece) CLI directly. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module github.com/gopher-fleece/gleece-runtime | ||
|
||
go 1.23.6 | ||
|
||
require github.com/go-playground/validator/v10 v10.24.0 | ||
|
||
require ( | ||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect | ||
github.com/go-playground/locales v0.14.1 // indirect | ||
github.com/go-playground/universal-translator v0.18.1 // indirect | ||
github.com/leodido/go-urn v1.4.0 // indirect | ||
golang.org/x/crypto v0.32.0 // indirect | ||
golang.org/x/net v0.34.0 // indirect | ||
golang.org/x/sys v0.29.0 // indirect | ||
golang.org/x/text v0.21.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= | ||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= | ||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | ||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= | ||
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= | ||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | ||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | ||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | ||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= | ||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package runtime | ||
|
||
type HttpStatusCode uint | ||
|
||
// Taken from net/http | ||
// Prefer to have this typed in-house as an 'enum' | ||
const ( | ||
StatusContinue HttpStatusCode = 100 // RFC 9110, 15.2.1 | ||
StatusSwitchingProtocols HttpStatusCode = 101 // RFC 9110, 15.2.2 | ||
StatusProcessing HttpStatusCode = 102 // RFC 2518, 10.1 | ||
StatusEarlyHints HttpStatusCode = 103 // RFC 8297 | ||
|
||
StatusOK HttpStatusCode = 200 // RFC 9110, 15.3.1 | ||
StatusCreated HttpStatusCode = 201 // RFC 9110, 15.3.2 | ||
StatusAccepted HttpStatusCode = 202 // RFC 9110, 15.3.3 | ||
StatusNonAuthoritativeInfo HttpStatusCode = 203 // RFC 9110, 15.3.4 | ||
StatusNoContent HttpStatusCode = 204 // RFC 9110, 15.3.5 | ||
StatusResetContent HttpStatusCode = 205 // RFC 9110, 15.3.6 | ||
StatusPartialContent HttpStatusCode = 206 // RFC 9110, 15.3.7 | ||
StatusMultiStatus HttpStatusCode = 207 // RFC 4918, 11.1 | ||
StatusAlreadyReported HttpStatusCode = 208 // RFC 5842, 7.1 | ||
StatusIMUsed HttpStatusCode = 226 // RFC 3229, 10.4.1 | ||
|
||
StatusMultipleChoices HttpStatusCode = 300 // RFC 9110, 15.4.1 | ||
StatusMovedPermanently HttpStatusCode = 301 // RFC 9110, 15.4.2 | ||
StatusFound HttpStatusCode = 302 // RFC 9110, 15.4.3 | ||
StatusSeeOther HttpStatusCode = 303 // RFC 9110, 15.4.4 | ||
StatusNotModified HttpStatusCode = 304 // RFC 9110, 15.4.5 | ||
StatusUseProxy HttpStatusCode = 305 // RFC 9110, 15.4.6 | ||
|
||
StatusTemporaryRedirect HttpStatusCode = 307 // RFC 9110, 15.4.8 | ||
StatusPermanentRedirect HttpStatusCode = 308 // RFC 9110, 15.4.9 | ||
|
||
StatusBadRequest HttpStatusCode = 400 // RFC 9110, 15.5.1 | ||
StatusUnauthorized HttpStatusCode = 401 // RFC 9110, 15.5.2 | ||
StatusPaymentRequired HttpStatusCode = 402 // RFC 9110, 15.5.3 | ||
StatusForbidden HttpStatusCode = 403 // RFC 9110, 15.5.4 | ||
StatusNotFound HttpStatusCode = 404 // RFC 9110, 15.5.5 | ||
StatusMethodNotAllowed HttpStatusCode = 405 // RFC 9110, 15.5.6 | ||
StatusNotAcceptable HttpStatusCode = 406 // RFC 9110, 15.5.7 | ||
StatusProxyAuthRequired HttpStatusCode = 407 // RFC 9110, 15.5.8 | ||
StatusRequestTimeout HttpStatusCode = 408 // RFC 9110, 15.5.9 | ||
StatusConflict HttpStatusCode = 409 // RFC 9110, 15.5.10 | ||
StatusGone HttpStatusCode = 410 // RFC 9110, 15.5.11 | ||
StatusLengthRequired HttpStatusCode = 411 // RFC 9110, 15.5.12 | ||
StatusPreconditionFailed HttpStatusCode = 412 // RFC 9110, 15.5.13 | ||
StatusRequestEntityTooLarge HttpStatusCode = 413 // RFC 9110, 15.5.14 | ||
StatusRequestURITooLong HttpStatusCode = 414 // RFC 9110, 15.5.15 | ||
StatusUnsupportedMediaType HttpStatusCode = 415 // RFC 9110, 15.5.16 | ||
StatusRequestedRangeNotSatisfiable HttpStatusCode = 416 // RFC 9110, 15.5.17 | ||
StatusExpectationFailed HttpStatusCode = 417 // RFC 9110, 15.5.18 | ||
StatusTeapot HttpStatusCode = 418 // RFC 9110, 15.5.19 (Unused) | ||
StatusMisdirectedRequest HttpStatusCode = 421 // RFC 9110, 15.5.20 | ||
StatusUnprocessableEntity HttpStatusCode = 422 // RFC 9110, 15.5.21 | ||
StatusLocked HttpStatusCode = 423 // RFC 4918, 11.3 | ||
StatusFailedDependency HttpStatusCode = 424 // RFC 4918, 11.4 | ||
StatusTooEarly HttpStatusCode = 425 // RFC 8470, 5.2. | ||
StatusUpgradeRequired HttpStatusCode = 426 // RFC 9110, 15.5.22 | ||
StatusPreconditionRequired HttpStatusCode = 428 // RFC 6585, 3 | ||
StatusTooManyRequests HttpStatusCode = 429 // RFC 6585, 4 | ||
StatusRequestHeaderFieldsTooLarge HttpStatusCode = 431 // RFC 6585, 5 | ||
StatusUnavailableForLegalReasons HttpStatusCode = 451 // RFC 7725, 3 | ||
|
||
StatusInternalServerError HttpStatusCode = 500 // RFC 9110, 15.6.1 | ||
StatusNotImplemented HttpStatusCode = 501 // RFC 9110, 15.6.2 | ||
StatusBadGateway HttpStatusCode = 502 // RFC 9110, 15.6.3 | ||
StatusServiceUnavailable HttpStatusCode = 503 // RFC 9110, 15.6.4 | ||
StatusGatewayTimeout HttpStatusCode = 504 // RFC 9110, 15.6.5 | ||
StatusHTTPVersionNotSupported HttpStatusCode = 505 // RFC 9110, 15.6.6 | ||
StatusVariantAlsoNegotiates HttpStatusCode = 506 // RFC 2295, 8.1 | ||
StatusInsufficientStorage HttpStatusCode = 507 // RFC 4918, 11.5 | ||
StatusLoopDetected HttpStatusCode = 508 // RFC 5842, 7.2 | ||
StatusNotExtended HttpStatusCode = 510 // RFC 2774, 7 | ||
StatusNetworkAuthenticationRequired HttpStatusCode = 511 // RFC 6585, 6 | ||
) | ||
|
||
type Rfc7807Error struct { | ||
Type string `json:"type"` | ||
Title string `json:"title"` | ||
Detail string `json:"detail"` | ||
Status int `json:"status"` | ||
Instance string `json:"instance"` | ||
Extensions map[string]string `json:"extensions"` | ||
} | ||
|
||
// GleeceController provides common functionality for controllers. | ||
type GleeceController struct { | ||
statusCode *HttpStatusCode | ||
headers map[string]string | ||
request any // Request is the HTTP request from the underlying routing engine (gin, echo etc.) | ||
} | ||
|
||
func (gc *GleeceController) InitController(request any) { | ||
gc.request = request | ||
gc.headers = make(map[string]string) | ||
} | ||
|
||
// SetStatus sets the status code for the GleeceController. | ||
func (gc *GleeceController) SetStatus(statusCode HttpStatusCode) { | ||
gc.statusCode = &statusCode | ||
} | ||
|
||
// GetStatus gets the status code for the GleeceController. | ||
func (gc *GleeceController) GetStatus() *HttpStatusCode { | ||
return gc.statusCode | ||
} | ||
|
||
// SetHeader sets a header for the GleeceController. | ||
func (gc *GleeceController) SetHeader(name string, value string) { | ||
gc.headers[name] = value | ||
} | ||
|
||
// GetHeaders get headers set (defined using the `SetHeader` API). | ||
func (gc *GleeceController) GetHeaders() map[string]string { | ||
return gc.headers | ||
} | ||
|
||
// GetContext returns the underlying request object (the type of the object is specific to the underlying routing engine). | ||
func (gc *GleeceController) GetContext() any { | ||
return gc.request | ||
} | ||
|
||
type Controller interface { | ||
InitController(request any) | ||
|
||
// SetStatus sets the status code for the GleeceController. | ||
SetStatus(statusCode HttpStatusCode) | ||
|
||
// GetStatus gets the status code for the GleeceController. | ||
GetStatus() *HttpStatusCode | ||
|
||
// SetHeader sets a header for the GleeceController. | ||
SetHeader(name string, value string) | ||
|
||
// GetHeaders get headers set (defined using the `SetHeader` API). | ||
GetHeaders() map[string]string | ||
|
||
// GetContext returns the underlying request object (the type of the object is specific to the underlying routing engine). | ||
GetContext() any | ||
} | ||
|
||
type SecurityCheck struct { | ||
SchemaName string `json:"name" validate:"required,starts_with_letter"` | ||
Scopes []string `json:"scopes" validate:"not_nil_array"` | ||
} | ||
|
||
type CustomError struct { | ||
Payload any | ||
} | ||
|
||
type SecurityError struct { | ||
Message string | ||
StatusCode HttpStatusCode | ||
CustomError *CustomError | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package runtime | ||
|
||
import ( | ||
"fmt" | ||
|
||
validator "github.com/go-playground/validator/v10" | ||
) | ||
|
||
func ExtractValidationErrorMessage(err error, fieldName *string) string { | ||
if err == nil { | ||
return "" | ||
} | ||
|
||
validationErrors, ok := err.(validator.ValidationErrors) | ||
if !ok { | ||
return err.Error() | ||
} | ||
|
||
var errStr string | ||
for _, validationErr := range validationErrors { | ||
fName := validationErr.Field() | ||
if fieldName != nil { | ||
fName = *fieldName | ||
} | ||
errStr += fmt.Sprintf("Field '%s' failed validation with tag '%s'. ", fName, validationErr.Tag()) | ||
} | ||
|
||
return errStr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package runtime | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/go-playground/validator/v10" | ||
) | ||
|
||
type TestStruct struct { | ||
Name string `validate:"required"` | ||
Email string `validate:"required,email"` | ||
Age int `validate:"required,gte=0,lte=130"` | ||
} | ||
|
||
func TestExtractValidationErrorMessage(t *testing.T) { | ||
validate := validator.New() | ||
|
||
tests := []struct { | ||
name string | ||
input error | ||
fieldName *string | ||
want string | ||
}{ | ||
{ | ||
name: "nil error", | ||
input: nil, | ||
fieldName: nil, | ||
want: "", | ||
}, | ||
{ | ||
name: "non-validation error", | ||
input: errors.New("regular error"), | ||
fieldName: nil, | ||
want: "regular error", | ||
}, | ||
{ | ||
name: "single validation error - required field", | ||
input: validate.Struct(TestStruct{}), | ||
fieldName: nil, | ||
want: "Field 'Name' failed validation with tag 'required'. Field 'Email' failed validation with tag 'required'. Field 'Age' failed validation with tag 'required'. ", | ||
}, | ||
{ | ||
name: "invalid email validation error", | ||
input: validate.Struct(TestStruct{Name: "John", Email: "invalid-email", Age: 25}), | ||
fieldName: nil, | ||
want: "Field 'Email' failed validation with tag 'email'. ", | ||
}, | ||
{ | ||
name: "validation error with custom field name", | ||
input: validate.Struct(TestStruct{Name: "John", Email: "invalid-email", Age: 25}), | ||
fieldName: strPtr("CustomField"), | ||
want: "Field 'CustomField' failed validation with tag 'email'. ", | ||
}, | ||
{ | ||
name: "age out of range validation", | ||
input: validate.Struct(TestStruct{Name: "John", Email: "[email protected]", Age: 150}), | ||
fieldName: nil, | ||
want: "Field 'Age' failed validation with tag 'lte'. ", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got := ExtractValidationErrorMessage(tt.input, tt.fieldName) | ||
if got != tt.want { | ||
t.Errorf("ExtractValidationErrorMessage() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// strPtr returns a pointer to the given string | ||
func strPtr(s string) *string { | ||
return &s | ||
} |
Oops, something went wrong.