diff --git a/app/boot_levels.go b/app/boot_levels.go index 708b281fb..1bbda9d3b 100644 --- a/app/boot_levels.go +++ b/app/boot_levels.go @@ -723,6 +723,7 @@ func updatePasswdSettings(opt options.AuthOpt, current *types.AppSettings) { func updateApigwSettings(opt options.ApigwOpt, current *types.AppSettings) { current.Apigw.ProfilerEnabled = opt.ProfilerEnabled + current.Apigw.ProfilerGlobal = opt.ProfilerGlobal } // Checks if discovery is enabled in the options diff --git a/app/options/apigw.cue b/app/options/apigw.cue index 4096f9c61..39993aa2f 100644 --- a/app/options/apigw.cue +++ b/app/options/apigw.cue @@ -29,6 +29,10 @@ apigw: schema.#optionsGroup & { type: "bool" description: "Enable profiler" } + profiler_global: { + type: "bool" + description: "Profiler enabled for all routes" + } log_request_body: { type: "bool" description: "Enable incoming request body output in logs" diff --git a/pkg/apigw/filter/postfilter.go b/pkg/apigw/filter/postfilter.go index 8710f4681..044f1e100 100644 --- a/pkg/apigw/filter/postfilter.go +++ b/pkg/apigw/filter/postfilter.go @@ -12,6 +12,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/apigw/types" errors "github.com/cortezaproject/corteza-server/pkg/errors" "github.com/cortezaproject/corteza-server/pkg/expr" + "github.com/cortezaproject/corteza-server/pkg/options" ) type ( @@ -55,7 +56,7 @@ type ( } ) -func NewRedirection() (e *redirection) { +func NewRedirection(opts *options.ApigwOpt) (e *redirection) { e = &redirection{} e.Name = "redirection" @@ -78,8 +79,12 @@ func NewRedirection() (e *redirection) { return } -func (h redirection) New() types.Handler { - return NewRedirection() +func (h redirection) New(opts *options.ApigwOpt) types.Handler { + return NewRedirection(opts) +} + +func (h redirection) Enabled() bool { + return true } func (h redirection) String() string { @@ -124,7 +129,7 @@ func (h redirection) Handler() types.HandlerFunc { } } -func NewDefaultJsonResponse() (e *defaultJsonResponse) { +func NewDefaultJsonResponse(opts *options.ApigwOpt) (e *defaultJsonResponse) { e = &defaultJsonResponse{} e.Name = "defaultJsonResponse" @@ -134,8 +139,12 @@ func NewDefaultJsonResponse() (e *defaultJsonResponse) { return } -func (j defaultJsonResponse) New() types.Handler { - return NewDefaultJsonResponse() +func (j defaultJsonResponse) New(opts *options.ApigwOpt) types.Handler { + return NewDefaultJsonResponse(opts) +} + +func (j defaultJsonResponse) Enabled() bool { + return true } func (j defaultJsonResponse) String() string { @@ -172,7 +181,7 @@ func checkStatus(typ string, status int) bool { } } -func NewJsonResponse(reg typesRegistry) (e *jsonResponse) { +func NewJsonResponse(opts *options.ApigwOpt, reg typesRegistry) (e *jsonResponse) { e = &jsonResponse{} e.Name = "jsonResponse" @@ -192,8 +201,12 @@ func NewJsonResponse(reg typesRegistry) (e *jsonResponse) { return } -func (j jsonResponse) New() types.Handler { - return NewJsonResponse(j.reg) +func (j jsonResponse) New(opts *options.ApigwOpt) types.Handler { + return NewJsonResponse(opts, j.reg) +} + +func (j jsonResponse) Enabled() bool { + return true } func (j jsonResponse) String() string { diff --git a/pkg/apigw/filter/postfilter_test.go b/pkg/apigw/filter/postfilter_test.go index a8753e7e7..2da9958ce 100644 --- a/pkg/apigw/filter/postfilter_test.go +++ b/pkg/apigw/filter/postfilter_test.go @@ -10,6 +10,7 @@ import ( agctx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx" "github.com/cortezaproject/corteza-server/pkg/apigw/types" "github.com/cortezaproject/corteza-server/pkg/expr" + "github.com/cortezaproject/corteza-server/pkg/options" "github.com/stretchr/testify/require" ) @@ -34,7 +35,7 @@ func Test_redirectionMerge(t *testing.T) { ) for _, tc := range tcc { - t.Run(tc.name, testMerge(NewRedirection(), tc)) + t.Run(tc.name, testMerge(NewRedirection(&options.ApigwOpt{}), tc)) } } @@ -74,7 +75,7 @@ func Test_redirection(t *testing.T) { rc = httptest.NewRecorder() ) - h := getHandler(NewRedirection()) + h := getHandler(NewRedirection(&options.ApigwOpt{})) h, err := h.Merge([]byte(tc.expr)) req.NoError(err) @@ -144,7 +145,7 @@ func Test_jsonResponse(t *testing.T) { r = r.WithContext(agctx.ScopeToContext(context.Background(), scope)) - h := getHandler(NewJsonResponse(&mockHandlerRegistry{})) + h := getHandler(NewJsonResponse(&options.ApigwOpt{}, &mockHandlerRegistry{})) h, err := h.Merge([]byte(tc.expr)) req.NoError(err) diff --git a/pkg/apigw/filter/prefilter.go b/pkg/apigw/filter/prefilter.go index 46a8a91e3..39d29407d 100644 --- a/pkg/apigw/filter/prefilter.go +++ b/pkg/apigw/filter/prefilter.go @@ -13,6 +13,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/apigw/types" pe "github.com/cortezaproject/corteza-server/pkg/errors" "github.com/cortezaproject/corteza-server/pkg/expr" + "github.com/cortezaproject/corteza-server/pkg/options" ) type ( @@ -41,11 +42,12 @@ type ( } profiler struct { + opts *options.ApigwOpt types.FilterMeta } ) -func NewHeader() (v *header) { +func NewHeader(opts *options.ApigwOpt) (v *header) { v = &header{} v.Name = "header" @@ -63,8 +65,12 @@ func NewHeader() (v *header) { return } -func (h header) New() types.Handler { - return NewHeader() +func (h header) New(opts *options.ApigwOpt) types.Handler { + return NewHeader(opts) +} + +func (h header) Enabled() bool { + return true } func (h header) String() string { @@ -129,7 +135,7 @@ func (h header) Handler() types.HandlerFunc { } } -func NewQueryParam() (v *queryParam) { +func NewQueryParam(opts *options.ApigwOpt) (v *queryParam) { v = &queryParam{} v.Name = "queryParam" @@ -147,8 +153,12 @@ func NewQueryParam() (v *queryParam) { return } -func (qp queryParam) New() types.Handler { - return NewQueryParam() +func (qp queryParam) New(opts *options.ApigwOpt) types.Handler { + return NewQueryParam(opts) +} + +func (qp queryParam) Enabled() bool { + return true } func (qp queryParam) String() string { @@ -213,9 +223,10 @@ func (qp *queryParam) Handler() types.HandlerFunc { } } -func NewProfiler() (pp *profiler) { +func NewProfiler(opts *options.ApigwOpt) (pp *profiler) { pp = &profiler{} + pp.opts = opts pp.Name = "profiler" pp.Label = "Profiler" pp.Kind = types.PreFilter @@ -223,8 +234,12 @@ func NewProfiler() (pp *profiler) { return } -func (pr profiler) New() types.Handler { - return NewProfiler() +func (pr profiler) New(opts *options.ApigwOpt) types.Handler { + return NewProfiler(opts) +} + +func (pr profiler) Enabled() bool { + return pr.opts.ProfilerEnabled } func (pr profiler) String() string { @@ -246,8 +261,8 @@ func (pr *profiler) Handler() types.HandlerFunc { scope = agctx.ScopeFromContext(ctx) ) - if scope.Opts().ProfilerEnabled { - // profiler enabled overrides any profiling prefilter + if pr.opts.ProfilerEnabled && pr.opts.ProfilerGlobal { + // profiler global overrides any profiling prefilter // the hit is registered on lower level return } diff --git a/pkg/apigw/filter/prefilter_test.go b/pkg/apigw/filter/prefilter_test.go index 97d4920d3..fbbb5b62f 100644 --- a/pkg/apigw/filter/prefilter_test.go +++ b/pkg/apigw/filter/prefilter_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/cortezaproject/corteza-server/pkg/apigw/types" + "github.com/cortezaproject/corteza-server/pkg/options" "github.com/stretchr/testify/require" ) @@ -33,7 +34,7 @@ func Test_headerMerge(t *testing.T) { ) for _, tc := range tcc { - t.Run(tc.name, testMerge(NewHeader(), tc)) + t.Run(tc.name, testMerge(NewHeader(&options.ApigwOpt{}), tc)) } } @@ -79,7 +80,7 @@ func Test_headerHandle(t *testing.T) { r := httptest.NewRequest(http.MethodGet, "/foo", http.NoBody) r.Header = tc.headers - t.Run(tc.name, testHandle(NewHeader(), r, tc)) + t.Run(tc.name, testHandle(NewHeader(&options.ApigwOpt{}), r, tc)) } } @@ -111,7 +112,7 @@ func Test_queryParamMerge(t *testing.T) { ) for _, tc := range tcc { - t.Run(tc.name, testMerge(NewQueryParam(), tc)) + t.Run(tc.name, testMerge(NewQueryParam(&options.ApigwOpt{}), tc)) } } @@ -144,7 +145,7 @@ func Test_queryParamHandle(t *testing.T) { for _, tc := range tcc { r := httptest.NewRequest(http.MethodGet, tc.url, http.NoBody) - t.Run(tc.name, testHandle(NewQueryParam(), r, tc)) + t.Run(tc.name, testHandle(NewQueryParam(&options.ApigwOpt{}), r, tc)) } } diff --git a/pkg/apigw/filter/processer.go b/pkg/apigw/filter/processer.go index 6d8e9ba23..679f231e8 100644 --- a/pkg/apigw/filter/processer.go +++ b/pkg/apigw/filter/processer.go @@ -15,6 +15,7 @@ import ( pe "github.com/cortezaproject/corteza-server/pkg/errors" "github.com/cortezaproject/corteza-server/pkg/expr" "github.com/cortezaproject/corteza-server/pkg/jsenv" + "github.com/cortezaproject/corteza-server/pkg/options" "go.uber.org/zap" ) @@ -47,7 +48,7 @@ type ( } ) -func NewWorkflow(wf WfExecer) (p *workflow) { +func NewWorkflow(opts *options.ApigwOpt, wf WfExecer) (p *workflow) { p = &workflow{} p.d = wf @@ -67,8 +68,12 @@ func NewWorkflow(wf WfExecer) (p *workflow) { return } -func (h workflow) New() types.Handler { - return NewWorkflow(h.d) +func (h workflow) New(opts *options.ApigwOpt) types.Handler { + return NewWorkflow(opts, h.d) +} + +func (h workflow) Enabled() bool { + return true } func (h workflow) String() string { @@ -147,7 +152,7 @@ func (h workflow) Handler() types.HandlerFunc { } } -func NewPayload(l *zap.Logger) (p *processerPayload) { +func NewPayload(opts *options.ApigwOpt, l *zap.Logger) (p *processerPayload) { p = &processerPayload{} p.vm = jsenv.New(jsenv.NewTransformer(jsenv.LoaderJS, jsenv.TargetES2016)) @@ -171,8 +176,12 @@ func NewPayload(l *zap.Logger) (p *processerPayload) { return } -func (h processerPayload) New() types.Handler { - return NewPayload(h.log) +func (h processerPayload) New(opts *options.ApigwOpt) types.Handler { + return NewPayload(opts, h.log) +} + +func (h processerPayload) Enabled() bool { + return true } func (h processerPayload) String() string { diff --git a/pkg/apigw/filter/processer_test.go b/pkg/apigw/filter/processer_test.go index 1a6f6ef95..0b81dae15 100644 --- a/pkg/apigw/filter/processer_test.go +++ b/pkg/apigw/filter/processer_test.go @@ -78,7 +78,7 @@ func Test_processerWorkflow(t *testing.T) { rc = httptest.NewRecorder() rq, _ = http.NewRequest("POST", "/foo", http.NoBody) ar, err = h.NewRequest(rq) - pp = NewWorkflow(tc.wfs) + pp = NewWorkflow(&options.ApigwOpt{}, tc.wfs) ) _, err = pp.Merge([]byte(tc.params)) @@ -173,7 +173,7 @@ func Test_processerPayload(t *testing.T) { ar, err = h.NewRequest(tc.rq) ) - pp := NewPayload(zap.NewNop()) + pp := NewPayload(&options.ApigwOpt{}, zap.NewNop()) _, err = pp.Merge([]byte(tc.params)) if tc.errv != "" { diff --git a/pkg/apigw/filter/proxy/proxy.go b/pkg/apigw/filter/proxy/proxy.go index 81d8c4dfe..3ed55d4db 100644 --- a/pkg/apigw/filter/proxy/proxy.go +++ b/pkg/apigw/filter/proxy/proxy.go @@ -14,6 +14,7 @@ import ( actx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx" "github.com/cortezaproject/corteza-server/pkg/apigw/types" pe "github.com/cortezaproject/corteza-server/pkg/errors" + "github.com/cortezaproject/corteza-server/pkg/options" "go.uber.org/zap" ) @@ -45,7 +46,7 @@ type ( } ) -func New(l *zap.Logger, c *http.Client, s types.SecureStorager) (p *proxy) { +func New(opts *options.ApigwOpt, l *zap.Logger, c *http.Client, s types.SecureStorager) (p *proxy) { p = &proxy{} p.c = c @@ -67,8 +68,12 @@ func New(l *zap.Logger, c *http.Client, s types.SecureStorager) (p *proxy) { return } -func (h proxy) New() types.Handler { - return New(h.log, h.c, h.s) +func (h proxy) New(opts *options.ApigwOpt) types.Handler { + return New(opts, h.log, h.c, h.s) +} + +func (h proxy) Enabled() bool { + return true } func (h proxy) String() string { diff --git a/pkg/apigw/filter/proxy/proxy_test.go b/pkg/apigw/filter/proxy/proxy_test.go index c8afd838b..5c6abbf63 100644 --- a/pkg/apigw/filter/proxy/proxy_test.go +++ b/pkg/apigw/filter/proxy/proxy_test.go @@ -174,7 +174,7 @@ func Test_proxy(t *testing.T) { rq = httptest.NewRequest("POST", "/foo", strings.NewReader(`custom request body`)) } - proxy := New(zap.NewNop(), c, struct{}{}) + proxy := New(&options.ApigwOpt{}, zap.NewNop(), c, struct{}{}) _, err := proxy.Merge([]byte(tc.params)) req.NoError(err) diff --git a/pkg/apigw/helpers.go b/pkg/apigw/helpers.go index 70705ec68..5a9cba2cf 100644 --- a/pkg/apigw/helpers.go +++ b/pkg/apigw/helpers.go @@ -41,7 +41,7 @@ func helperMethodNotAllowed(opt *options.ApigwOpt, pr *profiler.Profiler) http.H } func addToProfiler(opt *options.ApigwOpt, pr *profiler.Profiler, r *http.Request, status int) { - if !opt.ProfilerEnabled { + if !(opt.ProfilerEnabled && opt.ProfilerGlobal) { return } diff --git a/pkg/apigw/profiler/filter.go b/pkg/apigw/profiler/filter.go new file mode 100644 index 000000000..a5eaf23d8 --- /dev/null +++ b/pkg/apigw/profiler/filter.go @@ -0,0 +1,79 @@ +package profiler + +import ( + "encoding/base64" + + "github.com/cortezaproject/corteza-server/system/types" +) + +func FilterAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) { + var ( + dec string = "" + i uint = 0 + b = filter.Before == "" + ) + + if filter.Limit == 0 { + filter.Limit = FILTER_NUM_AGG_ITEMS + } + + dec, _ = decodeRoutePath(filter.Before) + + *list, _ = list.Filter(func(apa *types.ApigwProfilerAggregation) (bool, error) { + // after a specific hit and inside the limits + if b && i < filter.Limit { + i++ + filter.Next = encodeRoutePath(apa.Path) + return true, nil + } + + // after the specific hit check + if dec != "" && b == false { + b = apa.Path == dec + } + + return false, nil + }) + + return +} + +func FilterHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) { + var ( + i uint = 0 + b = filter.Before == "" + ) + + if filter.Limit == 0 { + filter.Limit = FILTER_NUM_ITEMS + } + + *list, _ = list.Filter(func(aph *types.ApigwProfilerHit) (bool, error) { + // after a specific hit and inside the limits + if b && i < filter.Limit { + i++ + filter.Next = aph.ID + return true, nil + } + + // after the specific hit check + if filter.Before != "" && b == false { + b = aph.ID == filter.Before + } + + return false, nil + }) + + return +} + +func encodeRoutePath(p string) string { + return base64.URLEncoding.EncodeToString([]byte(p)) +} + +func decodeRoutePath(p string) (s string, err error) { + b, err := base64.URLEncoding.DecodeString(p) + s = string(b) + + return +} diff --git a/pkg/apigw/profiler/handler.go b/pkg/apigw/profiler/handler.go new file mode 100644 index 000000000..f9d8dd8aa --- /dev/null +++ b/pkg/apigw/profiler/handler.go @@ -0,0 +1,16 @@ +package profiler + +import "net/http" + +func StartHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + // add some info to context + next.ServeHTTP(rw, r) + }) +} + +func FinishHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + next.ServeHTTP(rw, r) + }) +} diff --git a/pkg/apigw/profiler/profiler.go b/pkg/apigw/profiler/profiler.go index 17235cc21..e198b8658 100644 --- a/pkg/apigw/profiler/profiler.go +++ b/pkg/apigw/profiler/profiler.go @@ -4,11 +4,9 @@ import ( "encoding/base64" "fmt" "net/http" - "sort" "time" h "github.com/cortezaproject/corteza-server/pkg/http" - "github.com/cortezaproject/corteza-server/system/types" ) const ( @@ -19,9 +17,6 @@ const ( FILTER_NUM_AGG_ITEMS = 10 ) -var sortAggFields = []string{"path", "count", "size_min", "size_max", "size_avg", "time_min", "time_max", "time_avg"} -var sortRouteFields = []string{"time_start", "time_finish", "time_duration"} - type ( Hits map[string][]*Hit @@ -48,13 +43,6 @@ type ( Ts *time.Time Tf *time.Time } - - Sort struct { - Hit string - Path string - Size uint64 - Before string - } ) func New() *Profiler { @@ -89,7 +77,7 @@ func (p *Profiler) Push(h *Hit) (id string) { return } -func (p *Profiler) Dump(s Sort) Hits { +func (p *Profiler) Hits(s Sort) Hits { ll := p.l.Filter(func(k string, v *Hit) bool { var b bool = true @@ -131,19 +119,6 @@ func (s Hits) Filter(fn func(k string, v *Hit) bool) Hits { return ss } -func StartHandler(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - // add some info to context - next.ServeHTTP(rw, r) - }) -} - -func FinishHandler(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - next.ServeHTTP(rw, r) - }) -} - func (h Hits) Len() int { return h.Len() } @@ -155,147 +130,3 @@ func (h Hits) Less(i, j int) bool { func (h Hits) Swap(i, j int) { return } - -func SortAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) { - for _, ff := range sortAggFields { - fe := filter.Sort.Get(ff) - - if fe == nil { - continue - } - - if filter.Sort.Get(ff).Descending { - sort.Sort(sort.Reverse(getSortType(ff, list))) - break - } - - sort.Sort(getSortType(ff, list)) - break - } -} - -func FilterAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) { - var ( - dec string = "" - i uint = 0 - b = filter.Before == "" - ) - - if filter.Limit == 0 { - filter.Limit = FILTER_NUM_AGG_ITEMS - } - - dec, _ = decodeRoutePath(filter.Before) - - *list, _ = list.Filter(func(apa *types.ApigwProfilerAggregation) (bool, error) { - // after a specific hit and inside the limits - if b && i < filter.Limit { - i++ - filter.Next = encodeRoutePath(apa.Path) - return true, nil - } - - // after the specific hit check - if dec != "" && b == false { - b = apa.Path == dec - } - - return false, nil - }) - - return -} - -func FilterHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) { - var ( - i uint = 0 - b = filter.Before == "" - ) - - if filter.Limit == 0 { - filter.Limit = FILTER_NUM_ITEMS - } - - *list, _ = list.Filter(func(aph *types.ApigwProfilerHit) (bool, error) { - // after a specific hit and inside the limits - if b && i < filter.Limit { - i++ - filter.Next = aph.ID - return true, nil - } - - // after the specific hit check - if filter.Before != "" && b == false { - b = aph.ID == filter.Before - } - - return false, nil - }) - - return -} - -func SortHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) { - for _, ff := range sortRouteFields { - fe := filter.Sort.Get(ff) - - if fe == nil { - continue - } - - if filter.Sort.Get(ff).Descending { - sort.Sort(sort.Reverse(getSortTypeHit(ff, list))) - break - } - - sort.Sort(getSortTypeHit(ff, list)) - break - } -} - -func getSortType(s string, list *types.ApigwProfilerAggregationSet) sort.Interface { - switch s { - case "path": - return types.ByPath(*list) - case "count": - return types.ByCount(*list) - case "size_min": - return types.BySizeMin(*list) - case "size_max": - return types.BySizeMax(*list) - case "size_avg": - return types.BySizeAvg(*list) - case "time_min": - return types.ByTimeMin(*list) - case "time_max": - return types.ByTimeMax(*list) - case "time_avg": - return types.ByTimeAvg(*list) - default: - return types.ByCount(*list) - } -} - -func getSortTypeHit(s string, list *types.ApigwProfilerHitSet) sort.Interface { - switch s { - case "time_start": - return types.BySTime(*list) - case "time_finish": - return types.ByFTime(*list) - case "time_duration": - return types.ByDuration(*list) - default: - return types.BySTime(*list) - } -} - -func encodeRoutePath(p string) string { - return base64.URLEncoding.EncodeToString([]byte(p)) -} - -func decodeRoutePath(p string) (s string, err error) { - b, err := base64.URLEncoding.DecodeString(p) - s = string(b) - - return -} diff --git a/pkg/apigw/profiler/sort.go b/pkg/apigw/profiler/sort.go new file mode 100644 index 000000000..9de144f1f --- /dev/null +++ b/pkg/apigw/profiler/sort.go @@ -0,0 +1,93 @@ +package profiler + +import ( + "sort" + + "github.com/cortezaproject/corteza-server/system/types" +) + +var ( + sortAggFields = []string{"path", "count", "size_min", "size_max", "size_avg", "time_min", "time_max", "time_avg"} + sortRouteFields = []string{"time_start", "time_finish", "time_duration"} +) + +type ( + Sort struct { + Hit string + Path string + Size uint64 + Before string + } +) + +func SortAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) { + for _, ff := range sortAggFields { + fe := filter.Sort.Get(ff) + + if fe == nil { + continue + } + + if filter.Sort.Get(ff).Descending { + sort.Sort(sort.Reverse(getSortType(ff, list))) + break + } + + sort.Sort(getSortType(ff, list)) + break + } +} + +func SortHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) { + for _, ff := range sortRouteFields { + fe := filter.Sort.Get(ff) + + if fe == nil { + continue + } + + if filter.Sort.Get(ff).Descending { + sort.Sort(sort.Reverse(getSortTypeHit(ff, list))) + break + } + + sort.Sort(getSortTypeHit(ff, list)) + break + } +} + +func getSortType(s string, list *types.ApigwProfilerAggregationSet) sort.Interface { + switch s { + case "path": + return types.ByPath(*list) + case "count": + return types.ByCount(*list) + case "size_min": + return types.BySizeMin(*list) + case "size_max": + return types.BySizeMax(*list) + case "size_avg": + return types.BySizeAvg(*list) + case "time_min": + return types.ByTimeMin(*list) + case "time_max": + return types.ByTimeMax(*list) + case "time_avg": + return types.ByTimeAvg(*list) + default: + return types.ByCount(*list) + } +} + +func getSortTypeHit(s string, list *types.ApigwProfilerHitSet) sort.Interface { + switch s { + case "time_start": + return types.BySTime(*list) + case "time_finish": + return types.ByFTime(*list) + case "time_duration": + return types.ByDuration(*list) + default: + return types.BySTime(*list) + } +} diff --git a/pkg/apigw/registry/registry.go b/pkg/apigw/registry/registry.go index a7bd685b7..746d7a883 100644 --- a/pkg/apigw/registry/registry.go +++ b/pkg/apigw/registry/registry.go @@ -8,19 +8,22 @@ import ( "github.com/cortezaproject/corteza-server/pkg/apigw/filter" "github.com/cortezaproject/corteza-server/pkg/apigw/filter/proxy" "github.com/cortezaproject/corteza-server/pkg/apigw/types" + "github.com/cortezaproject/corteza-server/pkg/options" ) type ( Registry struct { - h map[string]types.Handler + opts *options.ApigwOpt + h map[string]types.Handler } secureStorageTodo struct{} ) -func NewRegistry() *Registry { +func NewRegistry(opts *options.ApigwOpt) *Registry { return &Registry{ - h: map[string]types.Handler{}, + h: map[string]types.Handler{}, + opts: opts, } } @@ -43,11 +46,15 @@ func (r *Registry) Get(identifier string) (types.Handler, error) { return nil, fmt.Errorf("could not get element from registry: %s", identifier) } - return f.New(), nil + return f.New(r.opts), nil } func (r *Registry) All() (list types.FilterMetaList) { for _, handler := range r.h { + if !handler.Enabled() { + continue + } + meta := handler.Meta() list = append(list, &meta) } @@ -57,19 +64,19 @@ func (r *Registry) All() (list types.FilterMetaList) { func (r *Registry) Preload() { // prefilters - r.Add("queryParam", filter.NewQueryParam()) - r.Add("header", filter.NewHeader()) - r.Add("profiler", filter.NewProfiler()) + r.Add("queryParam", filter.NewQueryParam(r.opts)) + r.Add("header", filter.NewHeader(r.opts)) + r.Add("profiler", filter.NewProfiler(r.opts)) // processers - r.Add("workflow", filter.NewWorkflow(NewWorkflow())) - r.Add("proxy", proxy.New(service.DefaultLogger, http.DefaultClient, secureStorageTodo{})) - r.Add("payload", filter.NewPayload(service.DefaultLogger)) + r.Add("workflow", filter.NewWorkflow(r.opts, NewWorkflow())) + r.Add("proxy", proxy.New(r.opts, service.DefaultLogger, http.DefaultClient, secureStorageTodo{})) + r.Add("payload", filter.NewPayload(r.opts, service.DefaultLogger)) // postfilters - r.Add("redirection", filter.NewRedirection()) - r.Add("jsonResponse", filter.NewJsonResponse(service.Registry())) - r.Add("defaultJsonResponse", filter.NewDefaultJsonResponse()) + r.Add("redirection", filter.NewRedirection(r.opts)) + r.Add("jsonResponse", filter.NewJsonResponse(r.opts, service.Registry())) + r.Add("defaultJsonResponse", filter.NewDefaultJsonResponse(r.opts)) } func NewWorkflow() (wf filter.WfExecer) { diff --git a/pkg/apigw/registry/registry_test.go b/pkg/apigw/registry/registry_test.go index 3d55b9b4a..3f3fe47dc 100644 --- a/pkg/apigw/registry/registry_test.go +++ b/pkg/apigw/registry/registry_test.go @@ -4,13 +4,14 @@ import ( "testing" "github.com/cortezaproject/corteza-server/pkg/apigw/types" + "github.com/cortezaproject/corteza-server/pkg/options" "github.com/stretchr/testify/require" ) func Test_registryAddGet(t *testing.T) { var ( req = require.New(t) - r = NewRegistry() + r = NewRegistry(&options.ApigwOpt{}) ) r.Add("mockHandler", types.MockHandler{}) @@ -25,7 +26,7 @@ func Test_registryAddGet(t *testing.T) { func Test_registryAddGetErr(t *testing.T) { var ( req = require.New(t) - r = NewRegistry() + r = NewRegistry(&options.ApigwOpt{}) ) r.Add("mockHandler", types.MockHandler{}) @@ -71,7 +72,7 @@ func Test_registryMerge(t *testing.T) { for _, tc := range tcc { var ( req = require.New(t) - r = NewRegistry() + r = NewRegistry(&options.ApigwOpt{}) ) m, err := r.Merge(types.MockHandler{}, []byte(tc.params)) @@ -89,7 +90,7 @@ func Test_registryMerge(t *testing.T) { func Test_registryAll(t *testing.T) { var ( req = require.New(t) - r = NewRegistry() + r = NewRegistry(&options.ApigwOpt{}) ) r.Add("mockHandler", types.MockHandler{}) diff --git a/pkg/apigw/route.go b/pkg/apigw/route.go index 8144d3340..8aaae1ccc 100644 --- a/pkg/apigw/route.go +++ b/pkg/apigw/route.go @@ -11,6 +11,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/auth" h "github.com/cortezaproject/corteza-server/pkg/http" "github.com/cortezaproject/corteza-server/pkg/options" + "github.com/davecgh/go-spew/spew" "go.uber.org/zap" ) @@ -54,8 +55,11 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) { scope.Set("opts", r.opts) scope.Set("request", ar) + spew.Dump("OPTS", r.opts) + // use profiler, override any profiling prefilter - if r.opts.ProfilerEnabled { + if r.opts.ProfilerEnabled && r.opts.ProfilerGlobal { + spew.Dump("adding to profiler") // add request to profiler hit = r.pr.Hit(ar) hit.Route = r.ID @@ -66,20 +70,28 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) { r.handler.ServeHTTP(w, req) - if r.opts.ProfilerEnabled { - r.pr.Push(hit) - } else { - if hit = actx.ProfilerFromContext(req.Context()).(*profiler.Hit); hit != nil && hit.R != nil { - // updated hit from a possible prefilter - // we need to push route ID even if the profiler is disabled - hit.Route = r.ID - r.pr.Push(hit) - } - } - r.log.Debug("finished serving route", zap.Duration("duration", time.Since(start)), ) + + if !r.opts.ProfilerEnabled { + return + } + + if r.opts.ProfilerGlobal { + r.pr.Push(hit) + return + } + + if hit = actx.ProfilerFromContext(req.Context()).(*profiler.Hit); hit != nil && hit.R != nil { + // updated hit from a possible prefilter + // we need to push route ID even if the profiler is disabled + hit.Route = r.ID + hit.Status = http.StatusOK + + r.pr.Push(hit) + r.log.Debug("pushing request to profiler") + } } func (r route) String() string { diff --git a/pkg/apigw/service.go b/pkg/apigw/service.go index d8a08d8b1..fdeb1cde4 100644 --- a/pkg/apigw/service.go +++ b/pkg/apigw/service.go @@ -59,7 +59,7 @@ func Setup(opts *options.ApigwOpt, log *zap.Logger, storer storer) { func New(opts *options.ApigwOpt, logger *zap.Logger, storer storer) *apigw { var ( pr = profiler.New() - reg = registry.NewRegistry() + reg = registry.NewRegistry(opts) ) reg.Preload() diff --git a/pkg/apigw/service_test.go b/pkg/apigw/service_test.go index a89901b6a..6e5943db3 100644 --- a/pkg/apigw/service_test.go +++ b/pkg/apigw/service_test.go @@ -7,6 +7,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/apigw/registry" "github.com/cortezaproject/corteza-server/pkg/apigw/types" + "github.com/cortezaproject/corteza-server/pkg/options" st "github.com/cortezaproject/corteza-server/system/types" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -159,7 +160,7 @@ func Test_serviceInit(t *testing.T) { ctx = context.Background() ) - reg := registry.NewRegistry() + reg := registry.NewRegistry(&options.ApigwOpt{}) for hn, h := range tc.reg { reg.Add(hn, h) diff --git a/pkg/apigw/types/handler.go b/pkg/apigw/types/handler.go index 3dddc5cea..f9d56930c 100644 --- a/pkg/apigw/types/handler.go +++ b/pkg/apigw/types/handler.go @@ -3,6 +3,8 @@ package types import ( "fmt" "net/http" + + "github.com/cortezaproject/corteza-server/pkg/options" ) type ( @@ -18,9 +20,10 @@ type ( HTTPHandler fmt.Stringer - New() Handler + New(*options.ApigwOpt) Handler Merge([]byte) (Handler, error) Meta() FilterMeta + Enabled() bool } HandlerFunc func(rw http.ResponseWriter, r *http.Request) error diff --git a/pkg/apigw/types/test.go b/pkg/apigw/types/test.go index ba21a1ca1..3968b3cea 100644 --- a/pkg/apigw/types/test.go +++ b/pkg/apigw/types/test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "net/http" + "github.com/cortezaproject/corteza-server/pkg/options" st "github.com/cortezaproject/corteza-server/system/types" ) @@ -32,10 +33,14 @@ type ( MockRoundTripper func(*http.Request) (*http.Response, error) ) -func (h MockHandler) New() Handler { +func (h MockHandler) New(opts *options.ApigwOpt) Handler { return MockHandler{} } +func (h MockHandler) Enabled() bool { + return true +} + func (h MockHandler) String() string { return "MockHandler" } diff --git a/pkg/options/options.gen.go b/pkg/options/options.gen.go index 38cd4c221..24c4b343a 100644 --- a/pkg/options/options.gen.go +++ b/pkg/options/options.gen.go @@ -86,6 +86,7 @@ type ( Debug bool `env:"APIGW_DEBUG"` LogEnabled bool `env:"APIGW_LOG_ENABLED"` ProfilerEnabled bool `env:"APIGW_PROFILER_ENABLED"` + ProfilerGlobal bool `env:"APIGW_PROFILER_GLOBAL"` LogRequestBody bool `env:"APIGW_LOG_REQUEST_BODY"` ProxyEnableDebugLog bool `env:"APIGW_PROXY_ENABLE_DEBUG_LOG"` ProxyFollowRedirects bool `env:"APIGW_PROXY_FOLLOW_REDIRECTS"` diff --git a/system/types/app_settings.go b/system/types/app_settings.go index fea4ac9e5..3cd826a67 100644 --- a/system/types/app_settings.go +++ b/system/types/app_settings.go @@ -212,6 +212,7 @@ type ( // This only holds the value of APIGW_PROFILER_ENABLED for now // ProfilerEnabled bool `kv:"-" json:"profilerEnabled"` + ProfilerGlobal bool `kv:"-" json:"profilerGlobal"` } `kv:"apigw" json:"apigw"` // UserInterface settings