mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
@ -23,8 +23,8 @@ const HTTP_REQUEST_LOG = gql`
|
||||
key
|
||||
value
|
||||
}
|
||||
status
|
||||
statusCode
|
||||
statusReason
|
||||
body
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ const HTTP_REQUEST_LOGS = gql`
|
||||
url
|
||||
timestamp
|
||||
response {
|
||||
status
|
||||
statusCode
|
||||
statusReason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,9 @@ function RequestListTable({
|
||||
{response && (
|
||||
<div>
|
||||
<HttpStatusIcon status={response.statusCode} />{" "}
|
||||
<code>{response.status}</code>
|
||||
<code>
|
||||
{response.statusCode} {response.statusReason}
|
||||
</code>
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
|
@ -8,7 +8,7 @@ interface Props {
|
||||
response: {
|
||||
proto: string;
|
||||
statusCode: number;
|
||||
status: string;
|
||||
statusReason: string;
|
||||
headers: Array<{ key: string; value: string }>;
|
||||
body?: string;
|
||||
};
|
||||
@ -42,7 +42,7 @@ function ResponseDetail({ response }: Props): JSX.Element {
|
||||
{response.proto}
|
||||
</Typography>
|
||||
</Typography>{" "}
|
||||
{response.status}
|
||||
{response.statusCode} {response.statusReason}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
|
3
go.mod
3
go.mod
@ -5,11 +5,14 @@ go 1.15
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.11.3
|
||||
github.com/GeertJohan/go.rice v1.0.0
|
||||
github.com/Masterminds/squirrel v1.4.0
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/websocket v1.4.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.1 // indirect
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/mattn/go-sqlite3 v1.14.4
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.0.1
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
)
|
||||
|
20
go.sum
20
go.sum
@ -5,6 +5,8 @@ github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/
|
||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
github.com/Masterminds/squirrel v1.4.0 h1:he5i/EXixZxrBUWcxzDYMiju9WZ3ld/l7QBNuo/eN3w=
|
||||
github.com/Masterminds/squirrel v1.4.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA=
|
||||
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
|
||||
@ -25,7 +27,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
|
||||
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
@ -40,17 +45,26 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg=
|
||||
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.4 h1:4rQjbDxdu9fSgI/r3KN72G3c2goxknAqHHgPWWs8UlI=
|
||||
github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@ -78,6 +92,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
||||
@ -96,6 +111,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -106,12 +122,16 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtD
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw=
|
||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM=
|
||||
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -59,12 +59,12 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
HTTPResponseLog struct {
|
||||
Body func(childComplexity int) int
|
||||
Headers func(childComplexity int) int
|
||||
Proto func(childComplexity int) int
|
||||
RequestID func(childComplexity int) int
|
||||
Status func(childComplexity int) int
|
||||
StatusCode func(childComplexity int) int
|
||||
Body func(childComplexity int) int
|
||||
Headers func(childComplexity int) int
|
||||
Proto func(childComplexity int) int
|
||||
RequestID func(childComplexity int) int
|
||||
StatusCode func(childComplexity int) int
|
||||
StatusReason func(childComplexity int) int
|
||||
}
|
||||
|
||||
Query struct {
|
||||
@ -191,13 +191,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.HTTPResponseLog.RequestID(childComplexity), true
|
||||
|
||||
case "HttpResponseLog.status":
|
||||
if e.complexity.HTTPResponseLog.Status == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.HTTPResponseLog.Status(childComplexity), true
|
||||
|
||||
case "HttpResponseLog.statusCode":
|
||||
if e.complexity.HTTPResponseLog.StatusCode == nil {
|
||||
break
|
||||
@ -205,6 +198,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.HTTPResponseLog.StatusCode(childComplexity), true
|
||||
|
||||
case "HttpResponseLog.statusReason":
|
||||
if e.complexity.HTTPResponseLog.StatusReason == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.HTTPResponseLog.StatusReason(childComplexity), true
|
||||
|
||||
case "Query.httpRequestLog":
|
||||
if e.complexity.Query.HTTPRequestLog == nil {
|
||||
break
|
||||
@ -288,8 +288,8 @@ var sources = []*ast.Source{
|
||||
type HttpResponseLog {
|
||||
requestId: ID!
|
||||
proto: String!
|
||||
status: String!
|
||||
statusCode: Int!
|
||||
statusReason: String!
|
||||
body: String
|
||||
headers: [HttpHeader!]!
|
||||
}
|
||||
@ -791,40 +791,6 @@ func (ec *executionContext) _HttpResponseLog_proto(ctx context.Context, field gr
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _HttpResponseLog_status(ctx context.Context, field graphql.CollectedField, obj *HTTPResponseLog) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "HttpResponseLog",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Status, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _HttpResponseLog_statusCode(ctx context.Context, field graphql.CollectedField, obj *HTTPResponseLog) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -859,6 +825,40 @@ func (ec *executionContext) _HttpResponseLog_statusCode(ctx context.Context, fie
|
||||
return ec.marshalNInt2int(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _HttpResponseLog_statusReason(ctx context.Context, field graphql.CollectedField, obj *HTTPResponseLog) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "HttpResponseLog",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.StatusReason, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _HttpResponseLog_body(ctx context.Context, field graphql.CollectedField, obj *HTTPResponseLog) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -2237,13 +2237,13 @@ func (ec *executionContext) _HttpResponseLog(ctx context.Context, sel ast.Select
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "status":
|
||||
out.Values[i] = ec._HttpResponseLog_status(ctx, field, obj)
|
||||
case "statusCode":
|
||||
out.Values[i] = ec._HttpResponseLog_statusCode(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "statusCode":
|
||||
out.Values[i] = ec._HttpResponseLog_statusCode(ctx, field, obj)
|
||||
case "statusReason":
|
||||
out.Values[i] = ec._HttpResponseLog_statusReason(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
|
@ -26,12 +26,12 @@ type HTTPRequestLog struct {
|
||||
}
|
||||
|
||||
type HTTPResponseLog struct {
|
||||
RequestID int64 `json:"requestId"`
|
||||
Proto string `json:"proto"`
|
||||
Status string `json:"status"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Body *string `json:"body"`
|
||||
Headers []HTTPHeader `json:"headers"`
|
||||
RequestID int64 `json:"requestId"`
|
||||
Proto string `json:"proto"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
StatusReason string `json:"statusReason"`
|
||||
Body *string `json:"body"`
|
||||
Headers []HTTPHeader `json:"headers"`
|
||||
}
|
||||
|
||||
type HTTPMethod string
|
||||
|
@ -5,6 +5,7 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dstotijn/hetty/pkg/reqlog"
|
||||
)
|
||||
@ -54,18 +55,21 @@ func (r *queryResolver) HTTPRequestLog(ctx context.Context, id int64) (*HTTPRequ
|
||||
|
||||
func parseRequestLog(req reqlog.Request) (HTTPRequestLog, error) {
|
||||
method := HTTPMethod(req.Request.Method)
|
||||
if !method.IsValid() {
|
||||
if method != "" && !method.IsValid() {
|
||||
return HTTPRequestLog{}, fmt.Errorf("request has invalid method: %v", method)
|
||||
}
|
||||
|
||||
log := HTTPRequestLog{
|
||||
ID: req.ID,
|
||||
URL: req.Request.URL.String(),
|
||||
Proto: req.Request.Proto,
|
||||
Method: method,
|
||||
Timestamp: req.Timestamp,
|
||||
}
|
||||
|
||||
if req.Request.URL != nil {
|
||||
log.URL = req.Request.URL.String()
|
||||
}
|
||||
|
||||
if len(req.Body) > 0 {
|
||||
reqBody := string(req.Body)
|
||||
log.Body = &reqBody
|
||||
@ -85,11 +89,14 @@ func parseRequestLog(req reqlog.Request) (HTTPRequestLog, error) {
|
||||
|
||||
if req.Response != nil {
|
||||
log.Response = &HTTPResponseLog{
|
||||
RequestID: req.ID,
|
||||
RequestID: req.Response.RequestID,
|
||||
Proto: req.Response.Response.Proto,
|
||||
Status: req.Response.Response.Status,
|
||||
StatusCode: req.Response.Response.StatusCode,
|
||||
}
|
||||
statusReasonSubs := strings.SplitN(req.Response.Response.Status, " ", 2)
|
||||
if len(statusReasonSubs) == 2 {
|
||||
log.Response.StatusReason = statusReasonSubs[1]
|
||||
}
|
||||
if len(req.Response.Body) > 0 {
|
||||
resBody := string(req.Response.Body)
|
||||
log.Response.Body = &resBody
|
||||
|
@ -12,8 +12,8 @@ type HttpRequestLog {
|
||||
type HttpResponseLog {
|
||||
requestId: ID!
|
||||
proto: String!
|
||||
status: String!
|
||||
statusCode: Int!
|
||||
statusReason: String!
|
||||
body: String
|
||||
headers: [HttpHeader!]!
|
||||
}
|
||||
|
@ -1,11 +1,82 @@
|
||||
package sqlite
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dstotijn/hetty/pkg/reqlog"
|
||||
)
|
||||
|
||||
type reqURL url.URL
|
||||
|
||||
type httpRequest struct {
|
||||
ID int64 `db:"req_id"`
|
||||
Proto string `db:"req_proto"`
|
||||
URL reqURL `db:"url"`
|
||||
Method string `db:"method"`
|
||||
Body []byte `db:"req_body"`
|
||||
Timestamp time.Time `db:"req_timestamp"`
|
||||
httpResponse
|
||||
}
|
||||
|
||||
type httpResponse struct {
|
||||
ID *int64
|
||||
Proto *string
|
||||
StatusCode *int
|
||||
Body *[]byte
|
||||
Timestamp *time.Time
|
||||
ID sql.NullInt64 `db:"res_id"`
|
||||
RequestID sql.NullInt64 `db:"res_req_id"`
|
||||
Proto sql.NullString `db:"res_proto"`
|
||||
StatusCode sql.NullInt64 `db:"status_code"`
|
||||
StatusReason sql.NullString `db:"status_reason"`
|
||||
Body []byte `db:"res_body"`
|
||||
Timestamp sql.NullTime `db:"res_timestamp"`
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer.
|
||||
func (u *reqURL) Scan(value interface{}) error {
|
||||
rawURL, ok := value.(string)
|
||||
if !ok {
|
||||
return errors.New("sqlite: cannot scan non-string value")
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sqlite: could not parse URL: %v", err)
|
||||
}
|
||||
|
||||
*u = reqURL(*parsed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dto httpRequest) toRequestLog() reqlog.Request {
|
||||
u := url.URL(dto.URL)
|
||||
reqLog := reqlog.Request{
|
||||
ID: dto.ID,
|
||||
Request: http.Request{
|
||||
Proto: dto.Proto,
|
||||
Method: dto.Method,
|
||||
URL: &u,
|
||||
},
|
||||
Body: dto.Body,
|
||||
Timestamp: dto.Timestamp,
|
||||
}
|
||||
|
||||
if dto.httpResponse.ID.Valid {
|
||||
reqLog.Response = &reqlog.Response{
|
||||
ID: dto.httpResponse.ID.Int64,
|
||||
RequestID: dto.httpResponse.RequestID.Int64,
|
||||
Response: http.Response{
|
||||
Status: strconv.FormatInt(dto.StatusCode.Int64, 10) + " " + dto.StatusReason.String,
|
||||
StatusCode: int(dto.StatusCode.Int64),
|
||||
Proto: dto.httpResponse.Proto.String,
|
||||
},
|
||||
Body: dto.httpResponse.Body,
|
||||
Timestamp: dto.httpResponse.Timestamp.Time,
|
||||
}
|
||||
}
|
||||
|
||||
return reqLog
|
||||
}
|
||||
|
@ -8,19 +8,29 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dstotijn/hetty/pkg/reqlog"
|
||||
"github.com/dstotijn/hetty/pkg/scope"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
// Register sqlite3 for use via database/sql.
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// Client implements reqlog.Repository.
|
||||
type Client struct {
|
||||
db *sql.DB
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
type httpRequestLogsQuery struct {
|
||||
requestCols []string
|
||||
requestHeaderCols []string
|
||||
responseHeaderCols []string
|
||||
joinResponse bool
|
||||
}
|
||||
|
||||
// New returns a new Client.
|
||||
@ -36,7 +46,7 @@ func New(filename string) (*Client, error) {
|
||||
opts.Set("_foreign_keys", "1")
|
||||
|
||||
dsn := fmt.Sprintf("file:%v?%v", filename, opts.Encode())
|
||||
db, err := sql.Open("sqlite3", dsn)
|
||||
db, err := sqlx.Open("sqlite3", dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -99,213 +109,107 @@ func (c *Client) Close() error {
|
||||
return c.db.Close()
|
||||
}
|
||||
|
||||
var reqFieldToColumnMap = map[string]string{
|
||||
"proto": "proto AS req_proto",
|
||||
"url": "url",
|
||||
"method": "method",
|
||||
"body": "body AS req_body",
|
||||
"timestamp": "timestamp AS req_timestamp",
|
||||
}
|
||||
|
||||
var resFieldToColumnMap = map[string]string{
|
||||
"requestId": "req_id AS res_req_id",
|
||||
"proto": "proto AS res_proto",
|
||||
"statusCode": "status_code",
|
||||
"statusReason": "status_reason",
|
||||
"body": "body AS res_body",
|
||||
"timestamp": "timestamp AS res_timestamp",
|
||||
}
|
||||
|
||||
var headerFieldToColumnMap = map[string]string{
|
||||
"key": "key",
|
||||
"value": "value",
|
||||
}
|
||||
|
||||
func (c *Client) FindRequestLogs(
|
||||
ctx context.Context,
|
||||
opts reqlog.FindRequestsOptions,
|
||||
scope *scope.Scope,
|
||||
) (reqLogs []reqlog.Request, err error) {
|
||||
// TODO: Pass GraphQL field collections upstream, so we can query only
|
||||
// requested fields.
|
||||
// TODO: Use opts and scope to filter.
|
||||
reqQuery := `SELECT
|
||||
req.id,
|
||||
req.proto,
|
||||
req.url,
|
||||
req.method,
|
||||
req.body,
|
||||
req.timestamp,
|
||||
res.id,
|
||||
res.proto,
|
||||
res.status_code,
|
||||
res.status_reason,
|
||||
res.body,
|
||||
res.timestamp
|
||||
FROM http_requests req
|
||||
LEFT JOIN http_responses res ON req.id = res.req_id
|
||||
ORDER BY req.id DESC`
|
||||
httpReqLogsQuery := parseHTTPRequestLogsQuery(ctx)
|
||||
|
||||
rows, err := c.db.QueryContext(ctx, reqQuery)
|
||||
reqQuery := sq.
|
||||
Select(httpReqLogsQuery.requestCols...).
|
||||
From("http_requests req").
|
||||
OrderBy("req.id DESC")
|
||||
if httpReqLogsQuery.joinResponse {
|
||||
reqQuery = reqQuery.LeftJoin("http_responses res ON req.id = res.req_id")
|
||||
}
|
||||
|
||||
sql, _, err := reqQuery.ToSql()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not parse query: %v", err)
|
||||
}
|
||||
|
||||
rows, err := c.db.QueryxContext(ctx, sql, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not execute query: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var reqLog reqlog.Request
|
||||
var resDTO httpResponse
|
||||
var statusReason *string
|
||||
var rawURL string
|
||||
|
||||
err := rows.Scan(
|
||||
&reqLog.ID,
|
||||
&reqLog.Request.Proto,
|
||||
&rawURL,
|
||||
&reqLog.Request.Method,
|
||||
&reqLog.Body,
|
||||
&reqLog.Timestamp,
|
||||
&resDTO.ID,
|
||||
&resDTO.Proto,
|
||||
&resDTO.StatusCode,
|
||||
&statusReason,
|
||||
&resDTO.Body,
|
||||
&resDTO.Timestamp,
|
||||
)
|
||||
var dto httpRequest
|
||||
err = rows.StructScan(&dto)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not scan row: %v", err)
|
||||
}
|
||||
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not parse URL: %v", err)
|
||||
}
|
||||
reqLog.Request.URL = u
|
||||
|
||||
if resDTO.ID != nil {
|
||||
status := strconv.Itoa(*resDTO.StatusCode) + " " + *statusReason
|
||||
reqLog.Response = &reqlog.Response{
|
||||
ID: *resDTO.ID,
|
||||
RequestID: reqLog.ID,
|
||||
Response: http.Response{
|
||||
Status: status,
|
||||
StatusCode: *resDTO.StatusCode,
|
||||
Proto: *resDTO.Proto,
|
||||
},
|
||||
Body: *resDTO.Body,
|
||||
Timestamp: *resDTO.Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
reqLogs = append(reqLogs, reqLog)
|
||||
reqLogs = append(reqLogs, dto.toRequestLog())
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not iterate over rows: %v", err)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
reqHeadersStmt, err := c.db.PrepareContext(ctx, `SELECT key, value FROM http_headers WHERE req_id = ?`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not prepare statement: %v", err)
|
||||
}
|
||||
defer reqHeadersStmt.Close()
|
||||
resHeadersStmt, err := c.db.PrepareContext(ctx, `SELECT key, value FROM http_headers WHERE res_id = ?`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not prepare statement: %v", err)
|
||||
}
|
||||
defer resHeadersStmt.Close()
|
||||
|
||||
for _, reqLog := range reqLogs {
|
||||
headers, err := findHeaders(ctx, reqHeadersStmt, reqLog.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not query request headers: %v", err)
|
||||
}
|
||||
reqLog.Request.Header = headers
|
||||
|
||||
if reqLog.Response != nil {
|
||||
headers, err := findHeaders(ctx, resHeadersStmt, reqLog.Response.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not query response headers: %v", err)
|
||||
}
|
||||
reqLog.Response.Response.Header = headers
|
||||
}
|
||||
if err := c.queryHeaders(ctx, httpReqLogsQuery, reqLogs); err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not query headers: %v", err)
|
||||
}
|
||||
|
||||
return reqLogs, nil
|
||||
}
|
||||
|
||||
func (c *Client) FindRequestLogByID(ctx context.Context, id int64) (reqlog.Request, error) {
|
||||
// TODO: Pass GraphQL field collections upstream, so we can query only
|
||||
// requested fields.
|
||||
reqQuery := `SELECT
|
||||
req.id,
|
||||
req.proto,
|
||||
req.url,
|
||||
req.method,
|
||||
req.body,
|
||||
req.timestamp,
|
||||
res.id,
|
||||
res.proto,
|
||||
res.status_code,
|
||||
res.status_reason,
|
||||
res.body,
|
||||
res.timestamp
|
||||
FROM http_requests req
|
||||
LEFT JOIN http_responses res ON req.id = res.req_id
|
||||
WHERE req_id = ?
|
||||
ORDER BY req.id DESC`
|
||||
httpReqLogsQuery := parseHTTPRequestLogsQuery(ctx)
|
||||
|
||||
var reqLog reqlog.Request
|
||||
var resDTO httpResponse
|
||||
var statusReason *string
|
||||
var rawURL string
|
||||
reqQuery := sq.
|
||||
Select(httpReqLogsQuery.requestCols...).
|
||||
From("http_requests req").
|
||||
Where("req.id = ?")
|
||||
if httpReqLogsQuery.joinResponse {
|
||||
reqQuery = reqQuery.LeftJoin("http_responses res ON req.id = res.req_id")
|
||||
}
|
||||
|
||||
err := c.db.QueryRowContext(ctx, reqQuery, id).Scan(
|
||||
&reqLog.ID,
|
||||
&reqLog.Request.Proto,
|
||||
&rawURL,
|
||||
&reqLog.Request.Method,
|
||||
&reqLog.Body,
|
||||
&reqLog.Timestamp,
|
||||
&resDTO.ID,
|
||||
&resDTO.Proto,
|
||||
&resDTO.StatusCode,
|
||||
&statusReason,
|
||||
&resDTO.Body,
|
||||
&resDTO.Timestamp,
|
||||
)
|
||||
reqSQL, _, err := reqQuery.ToSql()
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not parse query: %v", err)
|
||||
}
|
||||
|
||||
row := c.db.QueryRowxContext(ctx, reqSQL, id)
|
||||
var dto httpRequest
|
||||
err = row.StructScan(&dto)
|
||||
if err == sql.ErrNoRows {
|
||||
return reqlog.Request{}, reqlog.ErrRequestNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not scan row: %v", err)
|
||||
}
|
||||
reqLog := dto.toRequestLog()
|
||||
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not parse URL: %v", err)
|
||||
}
|
||||
reqLog.Request.URL = u
|
||||
|
||||
if resDTO.ID != nil {
|
||||
status := strconv.Itoa(*resDTO.StatusCode) + " " + *statusReason
|
||||
reqLog.Response = &reqlog.Response{
|
||||
ID: *resDTO.ID,
|
||||
RequestID: reqLog.ID,
|
||||
Response: http.Response{
|
||||
Status: status,
|
||||
StatusCode: *resDTO.StatusCode,
|
||||
Proto: *resDTO.Proto,
|
||||
},
|
||||
Body: *resDTO.Body,
|
||||
Timestamp: *resDTO.Timestamp,
|
||||
}
|
||||
reqLogs := []reqlog.Request{reqLog}
|
||||
if err := c.queryHeaders(ctx, httpReqLogsQuery, reqLogs); err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not query headers: %v", err)
|
||||
}
|
||||
|
||||
reqHeadersStmt, err := c.db.PrepareContext(ctx, `SELECT key, value FROM http_headers WHERE req_id = ?`)
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not prepare statement: %v", err)
|
||||
}
|
||||
defer reqHeadersStmt.Close()
|
||||
resHeadersStmt, err := c.db.PrepareContext(ctx, `SELECT key, value FROM http_headers WHERE res_id = ?`)
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not prepare statement: %v", err)
|
||||
}
|
||||
defer resHeadersStmt.Close()
|
||||
|
||||
headers, err := findHeaders(ctx, reqHeadersStmt, reqLog.ID)
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not query request headers: %v", err)
|
||||
}
|
||||
reqLog.Request.Header = headers
|
||||
|
||||
if reqLog.Response != nil {
|
||||
headers, err := findHeaders(ctx, resHeadersStmt, reqLog.Response.ID)
|
||||
if err != nil {
|
||||
return reqlog.Request{}, fmt.Errorf("sqlite: could not query response headers: %v", err)
|
||||
}
|
||||
reqLog.Response.Response.Header = headers
|
||||
}
|
||||
|
||||
return reqLog, nil
|
||||
return reqLogs[0], nil
|
||||
}
|
||||
|
||||
func (c *Client) AddRequestLog(
|
||||
@ -321,7 +225,7 @@ func (c *Client) AddRequestLog(
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
|
||||
tx, err := c.db.BeginTx(ctx, nil)
|
||||
tx, err := c.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sqlite: could not start transaction: %v", err)
|
||||
}
|
||||
@ -491,3 +395,103 @@ func findHeaders(ctx context.Context, stmt *sql.Stmt, id int64) (http.Header, er
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
func parseHTTPRequestLogsQuery(ctx context.Context) httpRequestLogsQuery {
|
||||
var joinResponse bool
|
||||
var reqHeaderCols, resHeaderCols []string
|
||||
|
||||
opCtx := graphql.GetOperationContext(ctx)
|
||||
reqFields := graphql.CollectFieldsCtx(ctx, nil)
|
||||
reqCols := []string{"req.id AS req_id", "res.id AS res_id"}
|
||||
|
||||
for _, reqField := range reqFields {
|
||||
if col, ok := reqFieldToColumnMap[reqField.Name]; ok {
|
||||
reqCols = append(reqCols, "req."+col)
|
||||
}
|
||||
if reqField.Name == "headers" {
|
||||
headerFields := graphql.CollectFields(opCtx, reqField.Selections, nil)
|
||||
for _, headerField := range headerFields {
|
||||
if col, ok := headerFieldToColumnMap[headerField.Name]; ok {
|
||||
reqHeaderCols = append(reqHeaderCols, col)
|
||||
}
|
||||
}
|
||||
}
|
||||
if reqField.Name == "response" {
|
||||
joinResponse = true
|
||||
resFields := graphql.CollectFields(opCtx, reqField.Selections, nil)
|
||||
for _, resField := range resFields {
|
||||
if resField.Name == "headers" {
|
||||
reqCols = append(reqCols, "res.id AS res_id")
|
||||
headerFields := graphql.CollectFields(opCtx, resField.Selections, nil)
|
||||
for _, headerField := range headerFields {
|
||||
if col, ok := headerFieldToColumnMap[headerField.Name]; ok {
|
||||
resHeaderCols = append(resHeaderCols, col)
|
||||
}
|
||||
}
|
||||
}
|
||||
if col, ok := resFieldToColumnMap[resField.Name]; ok {
|
||||
reqCols = append(reqCols, "res."+col)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return httpRequestLogsQuery{
|
||||
requestCols: reqCols,
|
||||
requestHeaderCols: reqHeaderCols,
|
||||
responseHeaderCols: resHeaderCols,
|
||||
joinResponse: joinResponse,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) queryHeaders(
|
||||
ctx context.Context,
|
||||
query httpRequestLogsQuery,
|
||||
reqLogs []reqlog.Request,
|
||||
) error {
|
||||
if len(query.requestHeaderCols) > 0 {
|
||||
reqHeadersQuery, _, err := sq.
|
||||
Select(query.requestHeaderCols...).
|
||||
From("http_headers").Where("req_id = ?").
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse request headers query: %v", err)
|
||||
}
|
||||
reqHeadersStmt, err := c.db.PrepareContext(ctx, reqHeadersQuery)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prepare statement: %v", err)
|
||||
}
|
||||
defer reqHeadersStmt.Close()
|
||||
for i := range reqLogs {
|
||||
headers, err := findHeaders(ctx, reqHeadersStmt, reqLogs[i].ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not query request headers: %v", err)
|
||||
}
|
||||
reqLogs[i].Request.Header = headers
|
||||
}
|
||||
}
|
||||
|
||||
if len(query.responseHeaderCols) > 0 {
|
||||
resHeadersQuery, _, err := sq.
|
||||
Select(query.responseHeaderCols...).
|
||||
From("http_headers").Where("res_id = ?").
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse response headers query: %v", err)
|
||||
}
|
||||
resHeadersStmt, err := c.db.PrepareContext(ctx, resHeadersQuery)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prepare statement: %v", err)
|
||||
}
|
||||
defer resHeadersStmt.Close()
|
||||
for i := range reqLogs {
|
||||
headers, err := findHeaders(ctx, resHeadersStmt, reqLogs[i].Response.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not query response headers: %v", err)
|
||||
}
|
||||
reqLogs[i].Response.Response.Header = headers
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user