2018-07-07 16:10:42 +00:00
|
|
|
package aicra
|
2018-05-21 10:02:24 +00:00
|
|
|
|
|
|
|
import (
|
2021-06-19 22:47:04 +00:00
|
|
|
"context"
|
2021-05-18 14:06:49 +00:00
|
|
|
"fmt"
|
2020-04-04 08:39:02 +00:00
|
|
|
"net/http"
|
2021-05-18 14:06:49 +00:00
|
|
|
"strings"
|
2019-04-30 22:02:28 +00:00
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
"github.com/xdrm-io/aicra/api"
|
|
|
|
"github.com/xdrm-io/aicra/internal/config"
|
|
|
|
"github.com/xdrm-io/aicra/internal/ctx"
|
|
|
|
"github.com/xdrm-io/aicra/internal/reqdata"
|
2018-05-21 10:02:24 +00:00
|
|
|
)
|
|
|
|
|
2021-03-28 17:03:16 +00:00
|
|
|
// Handler wraps the builder to handle requests
|
|
|
|
type Handler Builder
|
2020-04-04 08:36:52 +00:00
|
|
|
|
2021-04-18 14:50:02 +00:00
|
|
|
// ServeHTTP implements http.Handler and wraps it in middlewares (adapters)
|
2021-03-28 17:03:16 +00:00
|
|
|
func (s Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
2021-06-20 00:14:31 +00:00
|
|
|
var h http.Handler = http.HandlerFunc(s.resolve)
|
2021-04-18 14:50:02 +00:00
|
|
|
|
2021-06-20 00:14:31 +00:00
|
|
|
for _, mw := range s.middlewares {
|
|
|
|
h = mw(h)
|
2021-04-18 14:50:02 +00:00
|
|
|
}
|
2021-06-20 00:14:31 +00:00
|
|
|
h.ServeHTTP(w, r)
|
2021-04-18 14:50:02 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 00:14:31 +00:00
|
|
|
// ServeHTTP implements http.Handler and wraps it in middlewares (adapters)
|
2021-05-18 07:36:33 +00:00
|
|
|
func (s Handler) resolve(w http.ResponseWriter, r *http.Request) {
|
2021-06-20 19:52:43 +00:00
|
|
|
// match service from config
|
2021-03-28 17:03:16 +00:00
|
|
|
var service = s.conf.Find(r)
|
2020-04-04 09:50:01 +00:00
|
|
|
if service == nil {
|
2021-06-20 19:52:43 +00:00
|
|
|
newResponse().WithError(api.ErrUnknownService).ServeHTTP(w, r)
|
2020-04-04 09:50:01 +00:00
|
|
|
return
|
2018-07-06 08:49:52 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 00:14:31 +00:00
|
|
|
// extract request data
|
2021-03-28 17:03:16 +00:00
|
|
|
var input, err = extractInput(service, *r)
|
2020-04-04 09:50:01 +00:00
|
|
|
if err != nil {
|
2021-06-20 19:52:43 +00:00
|
|
|
newResponse().WithError(api.ErrMissingParam).ServeHTTP(w, r)
|
2020-04-04 09:50:01 +00:00
|
|
|
return
|
2020-04-04 09:46:37 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// match handler
|
2020-04-04 09:50:01 +00:00
|
|
|
var handler *apiHandler
|
2021-03-28 17:03:16 +00:00
|
|
|
for _, h := range s.handlers {
|
2020-04-04 09:50:01 +00:00
|
|
|
if h.Method == service.Method && h.Path == service.Pattern {
|
|
|
|
handler = h
|
2020-03-29 13:04:12 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-04 08:36:52 +00:00
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// no handler found
|
2020-04-04 09:50:01 +00:00
|
|
|
if handler == nil {
|
2021-06-20 19:52:43 +00:00
|
|
|
newResponse().WithError(api.ErrUncallableService).ServeHTTP(w, r)
|
2020-04-04 09:50:01 +00:00
|
|
|
return
|
2020-03-29 13:04:12 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// add info into context
|
2021-06-20 00:14:31 +00:00
|
|
|
c := r.Context()
|
|
|
|
c = context.WithValue(c, ctx.Request, r)
|
|
|
|
c = context.WithValue(c, ctx.Response, w)
|
|
|
|
c = context.WithValue(c, ctx.Auth, buildAuth(service.Scope, input.Data))
|
2021-05-18 07:59:49 +00:00
|
|
|
|
2021-06-20 00:14:31 +00:00
|
|
|
// create http handler
|
|
|
|
var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2021-06-20 19:52:43 +00:00
|
|
|
// should not happen
|
2021-06-20 08:24:12 +00:00
|
|
|
auth := api.GetAuth(r.Context())
|
|
|
|
if auth == nil {
|
2021-06-20 19:52:43 +00:00
|
|
|
newResponse().WithError(api.ErrPermission).ServeHTTP(w, r)
|
2021-06-20 08:24:12 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// reject non granted requests
|
|
|
|
if !auth.Granted() {
|
2021-06-20 19:52:43 +00:00
|
|
|
newResponse().WithError(api.ErrPermission).ServeHTTP(w, r)
|
2021-06-20 08:24:12 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// execute the service handler
|
2021-06-20 00:14:31 +00:00
|
|
|
s.handle(r.Context(), input, handler, service, w, r)
|
2021-05-18 07:59:49 +00:00
|
|
|
})
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// run contextual middlewares
|
2021-06-20 00:14:31 +00:00
|
|
|
for _, mw := range s.ctxMiddlewares {
|
|
|
|
h = mw(h)
|
2021-05-18 07:59:49 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// serve using the pre-filled context
|
2021-06-20 00:14:31 +00:00
|
|
|
h.ServeHTTP(w, r.WithContext(c))
|
2021-05-18 07:59:49 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// handle the service request with the associated handler func and respond using
|
|
|
|
// the handler func output
|
2021-06-20 00:14:31 +00:00
|
|
|
func (s *Handler) handle(c context.Context, input *reqdata.T, handler *apiHandler, service *config.Service, w http.ResponseWriter, r *http.Request) {
|
2021-06-20 19:52:43 +00:00
|
|
|
// pass execution to the handler function
|
2021-06-20 00:14:31 +00:00
|
|
|
var outData, outErr = handler.dyn.Handle(c, input.Data)
|
2021-06-19 22:47:04 +00:00
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// build response from output arguments
|
|
|
|
var res = newResponse().WithError(outErr)
|
2021-03-28 17:03:16 +00:00
|
|
|
for key, value := range outData {
|
2020-04-04 09:46:37 +00:00
|
|
|
|
2021-03-28 17:03:16 +00:00
|
|
|
// find original name from 'rename' field
|
2020-04-04 09:50:01 +00:00
|
|
|
for name, param := range service.Output {
|
|
|
|
if param.Rename == key {
|
2021-06-20 19:52:43 +00:00
|
|
|
res.SetValue(name, value)
|
2020-03-21 15:53:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-04 09:50:01 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 19:52:43 +00:00
|
|
|
// write response and close request
|
2021-03-28 17:03:16 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
|
|
res.ServeHTTP(w, r)
|
2019-09-26 17:03:37 +00:00
|
|
|
}
|
2020-04-04 10:05:17 +00:00
|
|
|
|
2021-03-28 17:03:16 +00:00
|
|
|
func extractInput(service *config.Service, req http.Request) (*reqdata.T, error) {
|
2021-03-28 16:49:23 +00:00
|
|
|
var dataset = reqdata.New(service)
|
2020-04-04 10:05:17 +00:00
|
|
|
|
2021-03-28 16:49:23 +00:00
|
|
|
// URI data
|
|
|
|
var err = dataset.GetURI(req)
|
2020-04-04 10:05:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-03-28 16:49:23 +00:00
|
|
|
// query data
|
2020-04-04 12:56:15 +00:00
|
|
|
err = dataset.GetQuery(req)
|
2020-04-04 10:05:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-03-28 16:49:23 +00:00
|
|
|
// form/json data
|
2020-04-04 12:56:15 +00:00
|
|
|
err = dataset.GetForm(req)
|
2020-04-04 10:05:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return dataset, nil
|
|
|
|
}
|
2021-06-20 00:14:31 +00:00
|
|
|
|
|
|
|
// buildAuth builds the api.Auth struct from the service scope configuration
|
|
|
|
//
|
|
|
|
// it replaces format '[a]' in scope where 'a' is an existing input argument's
|
|
|
|
// name with its value
|
|
|
|
func buildAuth(scope [][]string, in map[string]interface{}) *api.Auth {
|
|
|
|
updated := make([][]string, len(scope))
|
|
|
|
|
|
|
|
// replace '[arg_name]' with the 'arg_name' value if it is a known variable
|
|
|
|
// name
|
|
|
|
for a, list := range scope {
|
|
|
|
updated[a] = make([]string, len(list))
|
|
|
|
for b, perm := range list {
|
|
|
|
updated[a][b] = perm
|
|
|
|
for name, value := range in {
|
|
|
|
var (
|
|
|
|
token = fmt.Sprintf("[%s]", name)
|
|
|
|
replacement = ""
|
|
|
|
)
|
|
|
|
if value != nil {
|
|
|
|
replacement = fmt.Sprintf("[%v]", value)
|
|
|
|
}
|
|
|
|
updated[a][b] = strings.ReplaceAll(updated[a][b], token, replacement)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &api.Auth{
|
|
|
|
Required: updated,
|
|
|
|
Active: []string{},
|
|
|
|
}
|
|
|
|
}
|