Releases: xdrm-io/aicra
Pre-relase v0.6.0
v0.6.0-pre opti: remove the use for WithContext() with a custom context for auth…
Optimizations and refactor
differentiate input and output validators
Breaking changes:
Split the Builder.Validate()
method into:
Input(validator.Type)
for input typesOutput(string, reflect.Type)
for output types, as they do not require validation
migrate to the standard multipart reader
Do not use the custom-made multipart request reader as it fails on some binary files. Using the go standard library for consistency.
Check permissions before parameters
Only extract URI parameters as they are required to compute dynamic authorization.
Only extract other parameters (HTTP query and form) afterwards to:
- avoid api discovery attacks by using helpful aicra errors to know what to feed an endpoint with
- optimize server load by avoiding parsing parameters that can be heavy when a user is not authorized
add argument error information
Add field names in error descriptions about arguments : "missing parameter" and "invalid parameter".
fix response data names
Fix output data names passed to the responder. Use the name field from the configuration.
Better API
improve code coverage, fixes, limit api
Improved test coverage to +80% for every package and applied test-related fixes.
API cleanup and refactors.
Breaking changes
- remove
api.Response
- rename package
datatype
tovalidator
- merge package
builtin
intovalidator
- url encoded extraction (from request uri or body)
- if a single value is received, consider the value alone as a type
- otherwise : consider as a slice of values
- aicra handler differentiates missing and invalid parameters in its automatic responses
Internal changes
- export errors of packages to enable cross-package more specific tests
- aicra handler's request data extraction always checks for missing parameters (if mandatory)
some cases were ignored: no body, empty json, empty form-data, etc.
- reduce config.Service cyclomatic complexity, normalize method names and refactor
Preview 4
Breaking changes.
Going a bit deeper in the idiomatic go direction.
Service handlers
Service handler signature has changed in favor of:
func(context.Context, in) (*out, api.Err)
- the
context.Context
argument is the current http request's context. It contains pre-filled information accessible throughapi.GetRequest(ctx)
,api.GetResponseWriter(ctx)
andapi.GetAuth(ctx)
- the
in
argument is the input struct (unchanged) - the
out
argument is the output struct (unchanged) - the
api.Err
argument is the handler's error status (unchanged)
Middlewares
Custom middleware types have been dropped in favor of using func(api.Handler) api.Handler
.
standard
Standard http middlewares are added with builder.With()
and wrap the full http connection as you would expect an http middleware would. It can be used for logging, recovering requests, etc.
contextual
Authentication middlewares have been dropped in favor of a less specific type: contextual middlewares.
They only wrap the call to the service handler. The http request argument has a context pre-filled with useful information that is accessible through api.GetRequest(ctx)
, api.GetResponseWriter(ctx)
and api.GetAuth(ctx)
.
Migration example
Handlers before:
type Services struct{}
func (s Services) GetUsers(api.Ctx) (*[]UserModel, api.Err) {}
func (s Services) GetUserByID(api.Ctx, struct{ ID int }) (*UserModel, api.Err) {}
func (s Services) CreateUser(struct{ Name string }) (*UserModel, api.Err) {}
Handler after:
type Services struct{}
func (s Services) GetUsers(context.Context) (*[]UserModel, api.Err) {}
func (s Services) GetUserByID(context.Context, struct{ ID int }) (*UserModel, api.Err) {}
func (s Services) CreateUser(context.Context, struct{ Name string }) (*UserModel, api.Err) {}
Standard middlewares before:
builder := &aicra.Builder{}
err := builder.Setup(configReader)
if err != nil {
log.Fatalf("cannot setup aicra: %s", err)
}
builder.With(func(next api.AdapterFunc) api.AdapterFunc {
return func(w http.ResponseWriter, r *http.Request) {
next(r, w)
}
}
Standard middlewares after:
builder := &aicra.Builder{}
err := builder.Setup(configReader)
if err != nil {
log.Fatalf("cannot setup aicra: %s", err)
}
builder.With(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(r, w)
})
}
Authentication middlewares before:
builder := &aicra.Builder{}
err := builder.Setup(configReader)
if err != nil {
log.Fatalf("cannot setup aicra: %s", err)
}
builder.WithAuth(func(next api.AuthHandlerFunc) api.AuthHandlerFunc {
return func(a api.Auth, w http.ResponseWriter, r *http.Request) {
// add active request permission
a.Active = append(a.Active, "admin");
next(r, w)
}
}
Authentication middlewares after:
builder := &aicra.Builder{}
builder := &aicra.Builder{}
err := builder.Setup(configReader)
if err != nil {
log.Fatalf("cannot setup aicra: %s", err)
}
builder.WithContext(func(next http.Handler) http.Handler {
return http.Handlerfunc(func(w http.ResponseWriter, r *http.Request) {
// get auth from context
auth := api.GetAuth(r.Context());
// add active request permission
auth.Active = append(auth.Active, "admin");
next.ServeHTTP(r, w)
})
}