First rough version of proxy logs frontend

This commit is contained in:
David Stotijn
2020-09-20 22:30:17 +02:00
parent 211c11be2b
commit 21c78cdc23
22 changed files with 2244 additions and 1349 deletions

2
admin/next-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

10
admin/next.config.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
async rewrites() {
return [
{
source: "/api/:path",
destination: "http://localhost:8080/api/:path", // Matched parameters can be used in the destination
},
];
},
};

View File

@ -9,8 +9,22 @@
"export": "next build && next export -o build" "export": "next build && next export -o build"
}, },
"dependencies": { "dependencies": {
"next": "9.4.4", "@apollo/client": "^3.2.0",
"react": "16.13.1", "@material-ui/core": "^4.11.0",
"react-dom": "16.13.1" "@material-ui/icons": "^4.9.1",
"graphql": "^15.3.0",
"next": "^9.5.3",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-syntax-highlighter": "^13.5.3"
},
"devDependencies": {
"@types/node": "^14.11.1",
"@types/react": "^16.9.49",
"eslint": "^7.9.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"prettier": "^2.1.2",
"typescript": "^4.0.3"
} }
} }

View File

@ -1,3 +0,0 @@
const Page = () => <div>Gurp</div>
export default Page

View File

@ -0,0 +1,56 @@
import { gql, useQuery } from "@apollo/client";
import { Box, Grid, Paper } from "@material-ui/core";
import ResponseDetail from "./ResponseDetail";
import RequestDetail from "./RequestDetail";
const HTTP_REQUEST_LOG = gql`
query HttpRequestLog($id: ID!) {
httpRequestLog(id: $id) {
id
method
url
body
response {
proto
status
statusCode
body
}
}
}
`;
interface Props {
requestId: string;
}
function LogDetail({ requestId: id }: Props): JSX.Element {
const { loading, error, data } = useQuery(HTTP_REQUEST_LOG, {
variables: { id },
});
if (loading) return "Loading...";
if (error) return `Error: ${error.message}`;
const { method, url, body, response } = data.httpRequestLog;
return (
<div>
<Grid container item spacing={2}>
<Grid item xs={6}>
<Box component={Paper} m={2} maxHeight="63vh" overflow="scroll">
<RequestDetail request={{ method, url, body }} />
</Box>
</Grid>
<Grid item xs={6}>
<Box component={Paper} m={2} maxHeight="63vh" overflow="scroll">
<ResponseDetail response={response} />
</Box>
</Grid>
</Grid>
</div>
);
}
export default LogDetail;

View File

@ -0,0 +1,36 @@
import { Typography, Box } from "@material-ui/core";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
interface Props {
request: {
method: string;
url: string;
body?: string;
};
}
function RequestDetail({ request }: Props): JSX.Element {
return (
<div>
<Box m={3}>
<Typography variant="h5">
{request.method} {request.url}
</Typography>
</Box>
<Box>
{request.body && (
<SyntaxHighlighter
language="markup"
showLineNumbers={true}
style={materialLight}
>
{request.body}
</SyntaxHighlighter>
)}
</Box>
</div>
);
}
export default RequestDetail;

View File

@ -0,0 +1,58 @@
import { gql, useQuery } from "@apollo/client";
import {
TableContainer,
Paper,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
makeStyles,
} from "@material-ui/core";
const HTTP_REQUEST_LOGS = gql`
query HttpRequestLogs {
httpRequestLogs {
id
method
url
timestamp
}
}
`;
interface Props {
onLogClick(requestId: string): void;
}
function RequestList({ onLogClick }: Props): JSX.Element {
const { loading, error, data } = useQuery(HTTP_REQUEST_LOGS);
if (loading) return "Loading...";
if (error) return `Error: ${error.message}`;
const { httpRequestLogs: logs } = data;
return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Method</TableCell>
<TableCell>URL</TableCell>
</TableRow>
</TableHead>
<TableBody>
{logs.map(({ id, method, url }) => (
<TableRow key={id} onClick={() => onLogClick(id)}>
<TableCell>{method}</TableCell>
<TableCell>{url}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
export default RequestList;

View File

@ -0,0 +1,52 @@
import { Typography, Box } from "@material-ui/core";
import { green } from "@material-ui/core/colors";
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
interface Props {
response: {
proto: string;
statusCode: number;
status: string;
body?: string;
};
}
function ResponseDetail({ response }: Props): JSX.Element {
return (
<div>
<Box m={3}>
<Typography variant="h5">
{statusIcon(response.statusCode)} {response.proto} {response.status}
</Typography>
</Box>
<Box>
<SyntaxHighlighter
language="markup"
showLineNumbers={true}
style={materialLight}
>
{response.body}
</SyntaxHighlighter>
</Box>
</div>
);
}
function statusIcon(status: number): JSX.Element {
const style = { marginTop: ".2rem", verticalAlign: "top" };
switch (Math.floor(status / 100)) {
case 2:
case 3:
return <FiberManualRecordIcon style={{ ...style, color: green[400] }} />;
case 4:
return <FiberManualRecordIcon style={style} htmlColor={"#f00"} />;
case 5:
return <FiberManualRecordIcon style={style} htmlColor={"#f00"} />;
default:
return <FiberManualRecordIcon />;
}
}
export default ResponseDetail;

48
admin/src/lib/graphql.ts Normal file
View File

@ -0,0 +1,48 @@
import { useMemo } from "react";
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { concatPagination } from "@apollo/client/utilities";
let apolloClient;
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === "undefined",
link: new HttpLink({
uri: "http://localhost:3000/api/graphql",
}),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
allPosts: concatPagination(),
},
},
},
}),
});
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// gets hydrated here
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract();
// Restore the cache using the data passed from getStaticProps/getServerSideProps
// combined with the existing cached data
_apolloClient.cache.restore({ ...existingCache, ...initialState });
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === "undefined") return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState]);
return store;
}

7
admin/src/lib/theme.ts Normal file
View File

@ -0,0 +1,7 @@
import { createMuiTheme } from "@material-ui/core/styles";
import { red } from "@material-ui/core/colors";
// Create a theme instance.
const theme = createMuiTheme({});
export default theme;

41
admin/src/pages/_app.tsx Normal file
View File

@ -0,0 +1,41 @@
import React from "react";
import { AppProps } from "next/app";
import { ApolloProvider } from "@apollo/client";
import Head from "next/head";
import { ThemeProvider } from "@material-ui/core/styles";
import CssBaseline from "@material-ui/core/CssBaseline";
import theme from "../lib/theme";
import { useApollo } from "../lib/graphql";
function App({ Component, pageProps }: AppProps): JSX.Element {
const apolloClient = useApollo(pageProps.initialApolloState);
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<React.Fragment>
<Head>
<title>gurp</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<ApolloProvider client={apolloClient}>
<ThemeProvider theme={theme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</ApolloProvider>
</React.Fragment>
);
}
export default App;

View File

@ -0,0 +1,49 @@
import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheets } from "@material-ui/core/styles";
import theme from "../lib/theme";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<meta name="theme-color" content={theme.palette.primary.main} />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};

11
admin/src/pages/index.tsx Normal file
View File

@ -0,0 +1,11 @@
import RequestList from "../components/reqlog/RequestList";
function Index(): JSX.Element {
return (
<div>
<h1>gurp</h1>
</div>
);
}
export default Index;

View File

@ -0,0 +1,22 @@
import { useState } from "react";
import { Box } from "@material-ui/core";
import RequestList from "../../components/reqlog/RequestList";
import LogDetail from "../../components/reqlog/LogDetail";
function Logs(): JSX.Element {
const [detailReqLogId, setDetailReqLogId] = useState<string>();
const handleLogClick = (reqId: string) => setDetailReqLogId(reqId);
return (
<div>
<Box minHeight="375px" maxHeight="33vh" overflow="scroll">
<RequestList onLogClick={handleLogClick} />
</Box>
<Box>{detailReqLogId && <LogDetail requestId={detailReqLogId} />}</Box>
</div>
);
}
export default Logs;

19
admin/tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": ["dist", ".next", "out", "next.config.js"],
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx"]
}

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,7 @@ func main() {
adminRouter := router.MatcherFunc(func(req *http.Request, match *mux.RouteMatch) bool { adminRouter := router.MatcherFunc(func(req *http.Request, match *mux.RouteMatch) bool {
hostname, _ := os.Hostname() hostname, _ := os.Hostname()
host, _, _ := net.SplitHostPort(req.Host) host, _, _ := net.SplitHostPort(req.Host)
return strings.EqualFold(host, hostname) || req.Host == "gurp.proxy" return strings.EqualFold(host, hostname) || (req.Host == "gurp.proxy" || req.Host == "localhost:8080")
}).Subrouter() }).Subrouter()
// GraphQL server. // GraphQL server.

View File

@ -42,26 +42,32 @@ type DirectiveRoot struct {
} }
type ComplexityRoot struct { type ComplexityRoot struct {
HTTPRequest struct { HTTPRequestLog struct {
Body func(childComplexity int) int Body func(childComplexity int) int
ID func(childComplexity int) int
Method func(childComplexity int) int Method func(childComplexity int) int
Response func(childComplexity int) int Response func(childComplexity int) int
Timestamp func(childComplexity int) int Timestamp func(childComplexity int) int
URL func(childComplexity int) int URL func(childComplexity int) int
} }
HTTPResponse struct { HTTPResponseLog struct {
Body func(childComplexity int) int Body func(childComplexity int) int
Proto func(childComplexity int) int
RequestID func(childComplexity int) int
Status func(childComplexity int) int
StatusCode func(childComplexity int) int StatusCode func(childComplexity int) int
} }
Query struct { Query struct {
GetHTTPRequests func(childComplexity int) int HTTPRequestLog func(childComplexity int, id string) int
HTTPRequestLogs func(childComplexity int) int
} }
} }
type QueryResolver interface { type QueryResolver interface {
GetHTTPRequests(ctx context.Context) ([]HTTPRequest, error) HTTPRequestLog(ctx context.Context, id string) (*HTTPRequestLog, error)
HTTPRequestLogs(ctx context.Context) ([]HTTPRequestLog, error)
} }
type executableSchema struct { type executableSchema struct {
@ -79,61 +85,101 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
_ = ec _ = ec
switch typeName + "." + field { switch typeName + "." + field {
case "HttpRequest.body": case "HttpRequestLog.body":
if e.complexity.HTTPRequest.Body == nil { if e.complexity.HTTPRequestLog.Body == nil {
break break
} }
return e.complexity.HTTPRequest.Body(childComplexity), true return e.complexity.HTTPRequestLog.Body(childComplexity), true
case "HttpRequest.method": case "HttpRequestLog.id":
if e.complexity.HTTPRequest.Method == nil { if e.complexity.HTTPRequestLog.ID == nil {
break break
} }
return e.complexity.HTTPRequest.Method(childComplexity), true return e.complexity.HTTPRequestLog.ID(childComplexity), true
case "HttpRequest.response": case "HttpRequestLog.method":
if e.complexity.HTTPRequest.Response == nil { if e.complexity.HTTPRequestLog.Method == nil {
break break
} }
return e.complexity.HTTPRequest.Response(childComplexity), true return e.complexity.HTTPRequestLog.Method(childComplexity), true
case "HttpRequest.timestamp": case "HttpRequestLog.response":
if e.complexity.HTTPRequest.Timestamp == nil { if e.complexity.HTTPRequestLog.Response == nil {
break break
} }
return e.complexity.HTTPRequest.Timestamp(childComplexity), true return e.complexity.HTTPRequestLog.Response(childComplexity), true
case "HttpRequest.url": case "HttpRequestLog.timestamp":
if e.complexity.HTTPRequest.URL == nil { if e.complexity.HTTPRequestLog.Timestamp == nil {
break break
} }
return e.complexity.HTTPRequest.URL(childComplexity), true return e.complexity.HTTPRequestLog.Timestamp(childComplexity), true
case "HttpResponse.body": case "HttpRequestLog.url":
if e.complexity.HTTPResponse.Body == nil { if e.complexity.HTTPRequestLog.URL == nil {
break break
} }
return e.complexity.HTTPResponse.Body(childComplexity), true return e.complexity.HTTPRequestLog.URL(childComplexity), true
case "HttpResponse.statusCode": case "HttpResponseLog.body":
if e.complexity.HTTPResponse.StatusCode == nil { if e.complexity.HTTPResponseLog.Body == nil {
break break
} }
return e.complexity.HTTPResponse.StatusCode(childComplexity), true return e.complexity.HTTPResponseLog.Body(childComplexity), true
case "Query.getHttpRequests": case "HttpResponseLog.proto":
if e.complexity.Query.GetHTTPRequests == nil { if e.complexity.HTTPResponseLog.Proto == nil {
break break
} }
return e.complexity.Query.GetHTTPRequests(childComplexity), true return e.complexity.HTTPResponseLog.Proto(childComplexity), true
case "HttpResponseLog.requestId":
if e.complexity.HTTPResponseLog.RequestID == nil {
break
}
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
}
return e.complexity.HTTPResponseLog.StatusCode(childComplexity), true
case "Query.httpRequestLog":
if e.complexity.Query.HTTPRequestLog == nil {
break
}
args, err := ec.field_Query_httpRequestLog_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.HTTPRequestLog(childComplexity, args["id"].(string)), true
case "Query.httpRequestLogs":
if e.complexity.Query.HTTPRequestLogs == nil {
break
}
return e.complexity.Query.HTTPRequestLogs(childComplexity), true
} }
return 0, false return 0, false
@ -185,21 +231,26 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
} }
var sources = []*ast.Source{ var sources = []*ast.Source{
&ast.Source{Name: "pkg/api/schema.graphql", Input: `type HttpRequest { &ast.Source{Name: "pkg/api/schema.graphql", Input: `type HttpRequestLog {
id: ID!
url: String! url: String!
method: HttpMethod! method: HttpMethod!
body: String body: String
timestamp: Time! timestamp: Time!
response: HttpResponse response: HttpResponseLog
} }
type HttpResponse { type HttpResponseLog {
requestId: ID!
proto: String!
status: String!
statusCode: Int! statusCode: Int!
body: String body: String
} }
type Query { type Query {
getHttpRequests: [HttpRequest!]! httpRequestLog(id: ID!): HttpRequestLog
httpRequestLogs: [HttpRequestLog!]!
} }
enum HttpMethod { enum HttpMethod {
@ -237,6 +288,20 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
return args, nil return args, nil
} }
func (ec *executionContext) field_Query_httpRequestLog_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["id"]; ok {
arg0, err = ec.unmarshalNID2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["id"] = arg0
return args, nil
}
func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -273,7 +338,7 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl ***************************** // region **************************** field.gotpl *****************************
func (ec *executionContext) _HttpRequest_url(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) { func (ec *executionContext) _HttpRequestLog_id(ctx context.Context, field graphql.CollectedField, obj *HTTPRequestLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -281,7 +346,41 @@ func (ec *executionContext) _HttpRequest_url(ctx context.Context, field graphql.
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpRequest", Object: "HttpRequestLog",
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.ID, 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.marshalNID2string(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpRequestLog_url(ctx context.Context, field graphql.CollectedField, obj *HTTPRequestLog) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpRequestLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -307,7 +406,7 @@ func (ec *executionContext) _HttpRequest_url(ctx context.Context, field graphql.
return ec.marshalNString2string(ctx, field.Selections, res) return ec.marshalNString2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _HttpRequest_method(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) { func (ec *executionContext) _HttpRequestLog_method(ctx context.Context, field graphql.CollectedField, obj *HTTPRequestLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -315,7 +414,7 @@ func (ec *executionContext) _HttpRequest_method(ctx context.Context, field graph
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpRequest", Object: "HttpRequestLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -341,7 +440,7 @@ func (ec *executionContext) _HttpRequest_method(ctx context.Context, field graph
return ec.marshalNHttpMethod2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPMethod(ctx, field.Selections, res) return ec.marshalNHttpMethod2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPMethod(ctx, field.Selections, res)
} }
func (ec *executionContext) _HttpRequest_body(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) { func (ec *executionContext) _HttpRequestLog_body(ctx context.Context, field graphql.CollectedField, obj *HTTPRequestLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -349,7 +448,7 @@ func (ec *executionContext) _HttpRequest_body(ctx context.Context, field graphql
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpRequest", Object: "HttpRequestLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -372,7 +471,7 @@ func (ec *executionContext) _HttpRequest_body(ctx context.Context, field graphql
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _HttpRequest_timestamp(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) { func (ec *executionContext) _HttpRequestLog_timestamp(ctx context.Context, field graphql.CollectedField, obj *HTTPRequestLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -380,7 +479,7 @@ func (ec *executionContext) _HttpRequest_timestamp(ctx context.Context, field gr
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpRequest", Object: "HttpRequestLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -406,7 +505,7 @@ func (ec *executionContext) _HttpRequest_timestamp(ctx context.Context, field gr
return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
} }
func (ec *executionContext) _HttpRequest_response(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) { func (ec *executionContext) _HttpRequestLog_response(ctx context.Context, field graphql.CollectedField, obj *HTTPRequestLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -414,7 +513,7 @@ func (ec *executionContext) _HttpRequest_response(ctx context.Context, field gra
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpRequest", Object: "HttpRequestLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -432,12 +531,12 @@ func (ec *executionContext) _HttpRequest_response(ctx context.Context, field gra
if resTmp == nil { if resTmp == nil {
return graphql.Null return graphql.Null
} }
res := resTmp.(*HTTPResponse) res := resTmp.(*HTTPResponseLog)
fc.Result = res fc.Result = res
return ec.marshalOHttpResponse2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponse(ctx, field.Selections, res) return ec.marshalOHttpResponseLog2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponseLog(ctx, field.Selections, res)
} }
func (ec *executionContext) _HttpResponse_statusCode(ctx context.Context, field graphql.CollectedField, obj *HTTPResponse) (ret graphql.Marshaler) { func (ec *executionContext) _HttpResponseLog_requestId(ctx context.Context, field graphql.CollectedField, obj *HTTPResponseLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -445,7 +544,109 @@ func (ec *executionContext) _HttpResponse_statusCode(ctx context.Context, field
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpResponse", 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.RequestID, 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.marshalNID2string(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpResponseLog_proto(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.Proto, 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_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 {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpResponseLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -471,7 +672,7 @@ func (ec *executionContext) _HttpResponse_statusCode(ctx context.Context, field
return ec.marshalNInt2int(ctx, field.Selections, res) return ec.marshalNInt2int(ctx, field.Selections, res)
} }
func (ec *executionContext) _HttpResponse_body(ctx context.Context, field graphql.CollectedField, obj *HTTPResponse) (ret graphql.Marshaler) { func (ec *executionContext) _HttpResponseLog_body(ctx context.Context, field graphql.CollectedField, obj *HTTPResponseLog) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -479,7 +680,7 @@ func (ec *executionContext) _HttpResponse_body(ctx context.Context, field graphq
} }
}() }()
fc := &graphql.FieldContext{ fc := &graphql.FieldContext{
Object: "HttpResponse", Object: "HttpResponseLog",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: false,
@ -502,7 +703,45 @@ func (ec *executionContext) _HttpResponse_body(ctx context.Context, field graphq
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query_getHttpRequests(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_httpRequestLog(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_httpRequestLog_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().HTTPRequestLog(rctx, args["id"].(string))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*HTTPRequestLog)
fc.Result = res
return ec.marshalOHttpRequestLog2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_httpRequestLogs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -519,7 +758,7 @@ func (ec *executionContext) _Query_getHttpRequests(ctx context.Context, field gr
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().GetHTTPRequests(rctx) return ec.resolvers.Query().HTTPRequestLogs(rctx)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -531,9 +770,9 @@ func (ec *executionContext) _Query_getHttpRequests(ctx context.Context, field gr
} }
return graphql.Null return graphql.Null
} }
res := resTmp.([]HTTPRequest) res := resTmp.([]HTTPRequestLog)
fc.Result = res fc.Result = res
return ec.marshalNHttpRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestᚄ(ctx, field.Selections, res) return ec.marshalNHttpRequestLog2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@ -1668,36 +1907,41 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
// region **************************** object.gotpl **************************** // region **************************** object.gotpl ****************************
var httpRequestImplementors = []string{"HttpRequest"} var httpRequestLogImplementors = []string{"HttpRequestLog"}
func (ec *executionContext) _HttpRequest(ctx context.Context, sel ast.SelectionSet, obj *HTTPRequest) graphql.Marshaler { func (ec *executionContext) _HttpRequestLog(ctx context.Context, sel ast.SelectionSet, obj *HTTPRequestLog) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, httpRequestImplementors) fields := graphql.CollectFields(ec.OperationContext, sel, httpRequestLogImplementors)
out := graphql.NewFieldSet(fields) out := graphql.NewFieldSet(fields)
var invalids uint32 var invalids uint32
for i, field := range fields { for i, field := range fields {
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("HttpRequest") out.Values[i] = graphql.MarshalString("HttpRequestLog")
case "id":
out.Values[i] = ec._HttpRequestLog_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "url": case "url":
out.Values[i] = ec._HttpRequest_url(ctx, field, obj) out.Values[i] = ec._HttpRequestLog_url(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "method": case "method":
out.Values[i] = ec._HttpRequest_method(ctx, field, obj) out.Values[i] = ec._HttpRequestLog_method(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "body": case "body":
out.Values[i] = ec._HttpRequest_body(ctx, field, obj) out.Values[i] = ec._HttpRequestLog_body(ctx, field, obj)
case "timestamp": case "timestamp":
out.Values[i] = ec._HttpRequest_timestamp(ctx, field, obj) out.Values[i] = ec._HttpRequestLog_timestamp(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "response": case "response":
out.Values[i] = ec._HttpRequest_response(ctx, field, obj) out.Values[i] = ec._HttpRequestLog_response(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -1709,24 +1953,39 @@ func (ec *executionContext) _HttpRequest(ctx context.Context, sel ast.SelectionS
return out return out
} }
var httpResponseImplementors = []string{"HttpResponse"} var httpResponseLogImplementors = []string{"HttpResponseLog"}
func (ec *executionContext) _HttpResponse(ctx context.Context, sel ast.SelectionSet, obj *HTTPResponse) graphql.Marshaler { func (ec *executionContext) _HttpResponseLog(ctx context.Context, sel ast.SelectionSet, obj *HTTPResponseLog) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, httpResponseImplementors) fields := graphql.CollectFields(ec.OperationContext, sel, httpResponseLogImplementors)
out := graphql.NewFieldSet(fields) out := graphql.NewFieldSet(fields)
var invalids uint32 var invalids uint32
for i, field := range fields { for i, field := range fields {
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("HttpResponse") out.Values[i] = graphql.MarshalString("HttpResponseLog")
case "requestId":
out.Values[i] = ec._HttpResponseLog_requestId(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "proto":
out.Values[i] = ec._HttpResponseLog_proto(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "status":
out.Values[i] = ec._HttpResponseLog_status(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "statusCode": case "statusCode":
out.Values[i] = ec._HttpResponse_statusCode(ctx, field, obj) out.Values[i] = ec._HttpResponseLog_statusCode(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "body": case "body":
out.Values[i] = ec._HttpResponse_body(ctx, field, obj) out.Values[i] = ec._HttpResponseLog_body(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -1753,7 +2012,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("Query") out.Values[i] = graphql.MarshalString("Query")
case "getHttpRequests": case "httpRequestLog":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() { defer func() {
@ -1761,7 +2020,18 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
} }
}() }()
res = ec._Query_getHttpRequests(ctx, field) res = ec._Query_httpRequestLog(ctx, field)
return res
})
case "httpRequestLogs":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_httpRequestLogs(ctx, field)
if res == graphql.Null { if res == graphql.Null {
atomic.AddUint32(&invalids, 1) atomic.AddUint32(&invalids, 1)
} }
@ -2050,11 +2320,11 @@ func (ec *executionContext) marshalNHttpMethod2githubᚗcomᚋdstotijnᚋgurpᚋ
return v return v
} }
func (ec *executionContext) marshalNHttpRequest2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequest(ctx context.Context, sel ast.SelectionSet, v HTTPRequest) graphql.Marshaler { func (ec *executionContext) marshalNHttpRequestLog2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx context.Context, sel ast.SelectionSet, v HTTPRequestLog) graphql.Marshaler {
return ec._HttpRequest(ctx, sel, &v) return ec._HttpRequestLog(ctx, sel, &v)
} }
func (ec *executionContext) marshalNHttpRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestᚄ(ctx context.Context, sel ast.SelectionSet, v []HTTPRequest) graphql.Marshaler { func (ec *executionContext) marshalNHttpRequestLog2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx context.Context, sel ast.SelectionSet, v []HTTPRequestLog) graphql.Marshaler {
ret := make(graphql.Array, len(v)) ret := make(graphql.Array, len(v))
var wg sync.WaitGroup var wg sync.WaitGroup
isLen1 := len(v) == 1 isLen1 := len(v) == 1
@ -2078,7 +2348,7 @@ func (ec *executionContext) marshalNHttpRequest2ᚕgithubᚗcomᚋdstotijnᚋgur
if !isLen1 { if !isLen1 {
defer wg.Done() defer wg.Done()
} }
ret[i] = ec.marshalNHttpRequest2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequest(ctx, sel, v[i]) ret[i] = ec.marshalNHttpRequestLog2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx, sel, v[i])
} }
if isLen1 { if isLen1 {
f(i) f(i)
@ -2091,6 +2361,20 @@ func (ec *executionContext) marshalNHttpRequest2ᚕgithubᚗcomᚋdstotijnᚋgur
return ret return ret
} }
func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) {
return graphql.UnmarshalID(v)
}
func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {
res := graphql.MarshalID(v)
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
}
return res
}
func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) {
return graphql.UnmarshalInt(v) return graphql.UnmarshalInt(v)
} }
@ -2382,15 +2666,26 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
return ec.marshalOBoolean2bool(ctx, sel, *v) return ec.marshalOBoolean2bool(ctx, sel, *v)
} }
func (ec *executionContext) marshalOHttpResponse2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponse(ctx context.Context, sel ast.SelectionSet, v HTTPResponse) graphql.Marshaler { func (ec *executionContext) marshalOHttpRequestLog2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx context.Context, sel ast.SelectionSet, v HTTPRequestLog) graphql.Marshaler {
return ec._HttpResponse(ctx, sel, &v) return ec._HttpRequestLog(ctx, sel, &v)
} }
func (ec *executionContext) marshalOHttpResponse2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponse(ctx context.Context, sel ast.SelectionSet, v *HTTPResponse) graphql.Marshaler { func (ec *executionContext) marshalOHttpRequestLog2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestLog(ctx context.Context, sel ast.SelectionSet, v *HTTPRequestLog) graphql.Marshaler {
if v == nil { if v == nil {
return graphql.Null return graphql.Null
} }
return ec._HttpResponse(ctx, sel, v) return ec._HttpRequestLog(ctx, sel, v)
}
func (ec *executionContext) marshalOHttpResponseLog2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponseLog(ctx context.Context, sel ast.SelectionSet, v HTTPResponseLog) graphql.Marshaler {
return ec._HttpResponseLog(ctx, sel, &v)
}
func (ec *executionContext) marshalOHttpResponseLog2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponseLog(ctx context.Context, sel ast.SelectionSet, v *HTTPResponseLog) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._HttpResponseLog(ctx, sel, v)
} }
func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) {

View File

@ -9,15 +9,19 @@ import (
"time" "time"
) )
type HTTPRequest struct { type HTTPRequestLog struct {
URL string `json:"url"` ID string `json:"id"`
Method HTTPMethod `json:"method"` URL string `json:"url"`
Body *string `json:"body"` Method HTTPMethod `json:"method"`
Timestamp time.Time `json:"timestamp"` Body *string `json:"body"`
Response *HTTPResponse `json:"response"` Timestamp time.Time `json:"timestamp"`
Response *HTTPResponseLog `json:"response"`
} }
type HTTPResponse struct { type HTTPResponseLog struct {
RequestID string `json:"requestId"`
Proto string `json:"proto"`
Status string `json:"status"`
StatusCode int `json:"statusCode"` StatusCode int `json:"statusCode"`
Body *string `json:"body"` Body *string `json:"body"`
} }

View File

@ -4,6 +4,7 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/dstotijn/gurp/pkg/reqlog" "github.com/dstotijn/gurp/pkg/reqlog"
@ -17,37 +18,67 @@ type queryResolver struct{ *Resolver }
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
func (r *queryResolver) GetHTTPRequests(ctx context.Context) ([]HTTPRequest, error) { func (r *queryResolver) HTTPRequestLogs(ctx context.Context) ([]HTTPRequestLog, error) {
logs := r.RequestLogService.Requests() reqs := r.RequestLogService.FindAllRequests()
reqs := make([]HTTPRequest, len(logs)) logs := make([]HTTPRequestLog, len(reqs))
for i, log := range logs { for i, req := range reqs {
method := HTTPMethod(log.Request.Method) req, err := parseRequestLog(req)
if !method.IsValid() { if err != nil {
return nil, fmt.Errorf("request has invalid method: %v", method) return nil, err
} }
logs[i] = req
}
reqs[i] = HTTPRequest{ return logs, nil
URL: log.Request.URL.String(), }
Method: method,
Timestamp: log.Timestamp, func (r *queryResolver) HTTPRequestLog(ctx context.Context, id string) (*HTTPRequestLog, error) {
log, err := r.RequestLogService.FindRequestLogByID(id)
if err == reqlog.ErrRequestNotFound {
return nil, nil
}
if err != nil {
return nil, errors.New("could not get request by ID")
}
req, err := parseRequestLog(log)
if err != nil {
return nil, err
}
return &req, nil
}
func parseRequestLog(req reqlog.Request) (HTTPRequestLog, error) {
method := HTTPMethod(req.Request.Method)
if !method.IsValid() {
return HTTPRequestLog{}, fmt.Errorf("request has invalid method: %v", method)
}
log := HTTPRequestLog{
ID: req.ID.String(),
URL: req.Request.URL.String(),
Method: method,
Timestamp: req.Timestamp,
}
if len(req.Body) > 0 {
reqBody := string(req.Body)
log.Body = &reqBody
}
if req.Response != nil {
log.Response = &HTTPResponseLog{
RequestID: req.ID.String(),
Proto: req.Response.Response.Proto,
Status: req.Response.Response.Status,
StatusCode: req.Response.Response.StatusCode,
} }
if len(req.Response.Body) > 0 {
if len(log.Body) > 0 { resBody := string(req.Response.Body)
reqBody := string(log.Body) log.Response.Body = &resBody
reqs[i].Body = &reqBody
}
if log.Response != nil {
reqs[i].Response = &HTTPResponse{
StatusCode: log.Response.Response.StatusCode,
}
if len(log.Response.Body) > 0 {
resBody := string(log.Response.Body)
reqs[i].Response.Body = &resBody
}
} }
} }
return reqs, nil return log, nil
} }

View File

@ -1,18 +1,23 @@
type HttpRequest { type HttpRequestLog {
id: ID!
url: String! url: String!
method: HttpMethod! method: HttpMethod!
body: String body: String
timestamp: Time! timestamp: Time!
response: HttpResponse response: HttpResponseLog
} }
type HttpResponse { type HttpResponseLog {
requestId: ID!
proto: String!
status: String!
statusCode: Int! statusCode: Int!
body: String body: String
} }
type Query { type Query {
getHttpRequests: [HttpRequest!]! httpRequestLog(id: ID!): HttpRequestLog
httpRequestLogs: [HttpRequestLog!]!
} }
enum HttpMethod { enum HttpMethod {

View File

@ -15,6 +15,8 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
var ErrRequestNotFound = errors.New("reqlog: request not found")
type Request struct { type Request struct {
ID uuid.UUID ID uuid.UUID
Request http.Request Request http.Request
@ -40,12 +42,21 @@ func NewService() Service {
} }
} }
func (svc *Service) Requests() []Request { func (svc *Service) FindAllRequests() []Request {
// TODO(?): Is locking necessary here? return svc.store
}
func (svc *Service) FindRequestLogByID(id string) (Request, error) {
svc.mu.Lock() svc.mu.Lock()
defer svc.mu.Unlock() defer svc.mu.Unlock()
return svc.store for _, req := range svc.store {
if req.ID.String() == id {
return req, nil
}
}
return Request{}, ErrRequestNotFound
} }
func (svc *Service) addRequest(reqID uuid.UUID, req http.Request, body []byte) Request { func (svc *Service) addRequest(reqID uuid.UUID, req http.Request, body []byte) Request {