Add scope support

This commit is contained in:
David Stotijn
2020-10-29 20:54:17 +01:00
parent 98dacbe849
commit 0d04996f06
30 changed files with 2807 additions and 119 deletions

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,14 @@ type HTTPRequestLog struct {
Response *HTTPResponseLog `json:"response"`
}
type HTTPRequestLogFilter struct {
OnlyInScope bool `json:"onlyInScope"`
}
type HTTPRequestLogFilterInput struct {
OnlyInScope *bool `json:"onlyInScope"`
}
type HTTPResponseLog struct {
RequestID int64 `json:"requestId"`
Proto string `json:"proto"`
@ -47,6 +55,28 @@ type Project struct {
IsActive bool `json:"isActive"`
}
type ScopeHeader struct {
Key *string `json:"key"`
Value *string `json:"value"`
}
type ScopeHeaderInput struct {
Key *string `json:"key"`
Value *string `json:"value"`
}
type ScopeRule struct {
URL *string `json:"url"`
Header *ScopeHeader `json:"header"`
Body *string `json:"body"`
}
type ScopeRuleInput struct {
URL *string `json:"url"`
Header *ScopeHeaderInput `json:"header"`
Body *string `json:"body"`
}
type HTTPMethod string
const (

View File

@ -5,17 +5,20 @@ package api
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/99designs/gqlgen/graphql"
"github.com/dstotijn/hetty/pkg/proj"
"github.com/dstotijn/hetty/pkg/reqlog"
"github.com/dstotijn/hetty/pkg/scope"
"github.com/vektah/gqlparser/v2/gqlerror"
)
type Resolver struct {
RequestLogService *reqlog.Service
ProjectService *proj.Service
ScopeService *scope.Scope
}
type queryResolver struct{ *Resolver }
@ -25,9 +28,8 @@ func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
func (r *queryResolver) HTTPRequestLogs(ctx context.Context) ([]HTTPRequestLog, error) {
opts := reqlog.FindRequestsOptions{OmitOutOfScope: false}
reqs, err := r.RequestLogService.FindRequests(ctx, opts)
if err == reqlog.ErrNoProject {
reqs, err := r.RequestLogService.FindRequests(ctx)
if err == proj.ErrNoProject {
return nil, &gqlerror.Error{
Path: graphql.GetPath(ctx),
Message: "No active project.",
@ -133,7 +135,7 @@ func parseRequestLog(req reqlog.Request) (HTTPRequestLog, error) {
}
func (r *mutationResolver) OpenProject(ctx context.Context, name string) (*Project, error) {
p, err := r.ProjectService.Open(name)
p, err := r.ProjectService.Open(ctx, name)
if err == proj.ErrInvalidName {
return nil, gqlerror.Errorf("Project name must only contain alphanumeric or space chars.")
}
@ -178,6 +180,19 @@ func (r *queryResolver) Projects(ctx context.Context) ([]Project, error) {
return projects, nil
}
func (r *queryResolver) Scope(ctx context.Context) ([]ScopeRule, error) {
rules := r.ScopeService.Rules()
return scopeToScopeRules(rules), nil
}
func regexpToStringPtr(r *regexp.Regexp) *string {
if r == nil {
return nil
}
s := r.String()
return &s
}
func (r *mutationResolver) CloseProject(ctx context.Context) (*CloseProjectResult, error) {
if err := r.ProjectService.Close(); err != nil {
return nil, fmt.Errorf("could not close project: %v", err)
@ -193,3 +208,107 @@ func (r *mutationResolver) DeleteProject(ctx context.Context, name string) (*Del
Success: true,
}, nil
}
func (r *mutationResolver) SetScope(ctx context.Context, input []ScopeRuleInput) ([]ScopeRule, error) {
rules := make([]scope.Rule, len(input))
for i, rule := range input {
u, err := stringPtrToRegexp(rule.URL)
if err != nil {
return nil, fmt.Errorf("invalid URL in scope rule: %v", err)
}
var headerKey, headerValue *regexp.Regexp
if rule.Header != nil {
headerKey, err = stringPtrToRegexp(rule.Header.Key)
if err != nil {
return nil, fmt.Errorf("invalid header key in scope rule: %v", err)
}
headerValue, err = stringPtrToRegexp(rule.Header.Key)
if err != nil {
return nil, fmt.Errorf("invalid header value in scope rule: %v", err)
}
}
body, err := stringPtrToRegexp(rule.Body)
if err != nil {
return nil, fmt.Errorf("invalid body in scope rule: %v", err)
}
rules[i] = scope.Rule{
URL: u,
Header: scope.Header{
Key: headerKey,
Value: headerValue,
},
Body: body,
}
}
if err := r.ScopeService.SetRules(ctx, rules); err != nil {
return nil, fmt.Errorf("could not set scope: %v", err)
}
return scopeToScopeRules(rules), nil
}
func (r *queryResolver) HTTPRequestLogFilter(ctx context.Context) (*HTTPRequestLogFilter, error) {
return findReqFilterToHTTPReqLogFilter(r.RequestLogService.FindReqsFilter), nil
}
func (r *mutationResolver) SetHTTPRequestLogFilter(
ctx context.Context,
input *HTTPRequestLogFilterInput,
) (*HTTPRequestLogFilter, error) {
filter := findRequestsFilterFromInput(input)
if err := r.RequestLogService.SetRequestLogFilter(ctx, filter); err != nil {
return nil, fmt.Errorf("could not set request log filter: %v", err)
}
empty := reqlog.FindRequestsFilter{}
if filter == empty {
return nil, nil
}
return findReqFilterToHTTPReqLogFilter(filter), nil
}
func stringPtrToRegexp(s *string) (*regexp.Regexp, error) {
if s == nil {
return nil, nil
}
return regexp.Compile(*s)
}
func scopeToScopeRules(rules []scope.Rule) []ScopeRule {
scopeRules := make([]ScopeRule, len(rules))
for i, rule := range rules {
scopeRules[i].URL = regexpToStringPtr(rule.URL)
if rule.Header.Key != nil || rule.Header.Value != nil {
scopeRules[i].Header = &ScopeHeader{
Key: regexpToStringPtr(rule.Header.Key),
Value: regexpToStringPtr(rule.Header.Value),
}
}
scopeRules[i].Body = regexpToStringPtr(rule.Body)
}
return scopeRules
}
func findRequestsFilterFromInput(input *HTTPRequestLogFilterInput) (filter reqlog.FindRequestsFilter) {
if input == nil {
return
}
if input.OnlyInScope != nil {
filter.OnlyInScope = *input.OnlyInScope
}
return
}
func findReqFilterToHTTPReqLogFilter(findReqFilter reqlog.FindRequestsFilter) *HTTPRequestLogFilter {
empty := reqlog.FindRequestsFilter{}
if findReqFilter == empty {
return nil
}
httpReqLogFilter := &HTTPRequestLogFilter{
OnlyInScope: findReqFilter.OnlyInScope,
}
return httpReqLogFilter
}

View File

@ -28,6 +28,28 @@ type Project {
isActive: Boolean!
}
type ScopeRule {
url: Regexp
header: ScopeHeader
body: Regexp
}
input ScopeRuleInput {
url: Regexp
header: ScopeHeaderInput
body: Regexp
}
type ScopeHeader {
key: Regexp
value: Regexp
}
input ScopeHeaderInput {
key: Regexp
value: Regexp
}
type CloseProjectResult {
success: Boolean!
}
@ -36,17 +58,31 @@ type DeleteProjectResult {
success: Boolean!
}
input HttpRequestLogFilterInput {
onlyInScope: Boolean
}
type HttpRequestLogFilter {
onlyInScope: Boolean!
}
type Query {
httpRequestLog(id: ID!): HttpRequestLog
httpRequestLogs: [HttpRequestLog!]!
httpRequestLogFilter: HttpRequestLogFilter
activeProject: Project
projects: [Project!]!
scope: [ScopeRule!]!
}
type Mutation {
openProject(name: String!): Project
closeProject: CloseProjectResult!
deleteProject(name: String!): DeleteProjectResult!
setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]!
setHttpRequestLogFilter(
filter: HttpRequestLogFilterInput
): HttpRequestLogFilter
}
enum HttpMethod {
@ -62,3 +98,4 @@ enum HttpMethod {
}
scalar Time
scalar Regexp