Closed podhmo closed 2 years ago
action function doesn't know request. but in action, login user is needed, maybe.
then using something like getLoginUser interface (or function) as component?
if so, get component in provider, handle with request as the argument of provider function?(currently not supported yet, primitive arguments and context.Context only)
or #26 recursive dependency is needed?
attaching loginRequired middleware is best. auth is web handler layer
It is cool and handy that authorization in the middleware phase. But this is not suitable with provider abstraction ( the code that middleware using the provided value by provider )
the code something like followings are not happy.
https://gist.github.com/podhmo/5f8fe705128f4b4e4145e157b62b0cf2#file-main-go-L44-L82
So, simply, adding option that adding external dependencies, something like below.
r.Get("/hello". Hello, web.WithExternalDependencies(LoginRequired))
then Login required is something like this.
func LoginRequired(db *DB) error {
... do something
}
and generated code is here
func Handler(getProvider func(*http.Request) (*http.Request, Provider, error)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
req, provider, err := getProvider(req)
if err != nil {
runtime.HandleResult(w, req, nil, err); return
}
var ctx context.Context = req.Context()
var db *genchi.DB
{
db = provider.DB()
}
if err := action.LoginRequired(db); err != nil { // added.
runtime.HandleResult(w, req, nil, err); return
}
result, err := action.Hello()
runtime.HandleResult(w, req, result, err)
}
}
need also request, right?
action.LoginRequired(db, req)
Of course, internal action requires loginUser. It is OK.
func Submit(loginUser *LoginUser, db *DB, data Data) (*SubmitOutput, error) {
...
}
type LoginUser User
// r.Post("/submit", Submit)
generated code is something like below.
var db *DB
...
var loginUser *LoginUser
{
var err error
loginUser, err = provider.LoginUser(db, req)
if err != nil {
...
}
}
...
result, err := action.Submit(loginUser, db, data)
runtime.HandleResult(w, req, result, err)
And LoginRequired() requires *loginUser.
func LoginRequired(loginUser *LoginUser) error {
if loginUser == nil {
return fmt.Errorf("401")
}
return nil
}
then generated code is changed.
// r.Get("/hello". Hello, web.WithExternalDependencies(LoginRequired))
func Handler(getProvider func(*http.Request) (*http.Request, Provider, error)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
req, provider, err := getProvider(req)
if err != nil {
runtime.HandleResult(w, req, nil, err)
return
}
var ctx context.Context = req.Context()
var db *genchi.DB
{
db = provider.DB()
}
var loginUser *LoginUser
{
var err error
loginUser, err = provider.LoginUser(db, req)
if err != nil {
runtime.HandleResult(w, req, nil, err)
return
}
}
if err := action.LoginRequired(loginUser); err != nil { // in this case, this if-statement is almost dead-code.
runtime.HandleResult(w, req, nil, err)
return
}
result, err := action.Hello()
runtime.HandleResult(w, req, result, err)
}
}
In addition, if you can create new individual additional metadata attachment option (e.g. the metadata for openAPI doc)
package yourpackage
var mu sync.Mutex
var metaInfoMap = map[*web.Node]*MetaInfo
func WithMetaInfo(info *MetaInfo) web.RoutingOption {
return func(node *web.Node) {
mu.Lock()
defer mu.Unlock()
metainfoMap[node] = info
}
}
And use it
r.Get("/foo/{fooId}", GetFoo, yourpackage.WithMetaInfo(&MetaInfo{Examples: "<example json>"}))
func WithLoginRequired() web.RoutingOption {
return func(node *web.Node) {
web.SetExternalDependencies(node, LoginRequired)
metaInfoMap[node].LoginRequired = true
}
}
then
r.Get("/foo/{fooId}", GetFoo, yourpackage.WithLoginRequired())
more comfortable name handling is #131
adding example is not using barer token, but feature is fulfilled enough.
meta #19