Skip to content

Commit 4e8573c

Browse files
Merge pull request #163 from rakutentech/feature/bugs
(v2.8) fixes display bugs, timeline for model events, update screenshots
2 parents 031b1a4 + 207577b commit 4e8573c

File tree

9 files changed

+146
-54
lines changed

9 files changed

+146
-54
lines changed

README.md

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<p align="center">
22
<a href="https://github.com/rakutentech/laravel-request-docs">
3-
<img alt="Laravel Request Docs" src="https://imgur.com/2NvLKn2.png">
3+
<img alt="Laravel Request Docs" src="https://imgur.com/NuQNx8v.png">
44
</a>
55
</p>
66

@@ -27,6 +27,8 @@
2727
- Support for SQL query and query time
2828
- Support for HTTP response time and memory consumption
2929
- Support for Authorization Headers
30+
- Support for File uploads
31+
- Support for Eloquents events
3032
- Display extra documentation using markdown
3133
- Saves history previous requests
3234
- Added filters to sort, group and filter routes by methods, controllers, middlewares, routes
@@ -76,31 +78,33 @@ For extra documentation you can use markdown inside your controller method as we
7678

7779
# Screenshots
7880

79-
**Light and Dark Modes**
81+
**Dark and Light Modes**
8082

81-
Light mode
82-
![Preview](https://imgur.com/2s1SrKm.png)
83-
84-
Dark mode
85-
![Preview](https://imgur.com/76sk7Lq.png)
86-
87-
**Try API**
88-
89-
Uses localstorage to save history of previous requests and request headers
90-
91-
![Preview](https://imgur.com/q3d7pw2.png)
83+
<p float="left">
84+
<img src="https://imgur.com/vOMMYVl.png" width="49%" />
85+
<img src="https://imgur.com/HZvNOFm.png" width="49%" />
86+
</p>
9287

93-
**SQL query profile**
9488

95-
![Preview](https://imgur.com/8PLLlHv.png)
89+
- Uses localstorage to save history of previous requests and request headers.
90+
- Request, sql, response and events timeline below:
9691

97-
**Response profile**
92+
<p float="left">
93+
<img src="https://imgur.com/fd09jw1.png" width="32%" />
94+
<img src="https://imgur.com/8PLLlHv.png" width="45%" />
95+
</p>
9896

99-
![Preview](https://imgur.com/fd09jw1.png)
97+
<p float="left">
98+
<img src="https://imgur.com/q3d7pw2.png" width="49%" />
99+
<img src="https://imgur.com/AHTCUOJ.png" width="41%" />
100+
</p>
100101

101102
**Settings to sort, group and filter**
102103

103-
![Preview](https://imgur.com/qHq1pjr.png)
104+
<p float="left">
105+
<img src="https://imgur.com/SGXlIbl.png" width="30%" />
106+
<img src="https://imgur.com/Wb2AmZl.png" width="25%" />
107+
</p>
104108

105109

106110
# Extra
@@ -188,6 +192,7 @@ Fixing lints
188192
- v2.5 Groupby final fix and localstorage clear button. Other UI refactor
189193
- v2.6 File uploads
190194
- v2.7 Show activity on Eloquent models
195+
- v2.8 Show full activity on Eloquent models
191196

192197

193198
# Maintainers

src/LaravelRequestDocsMiddleware.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class LaravelRequestDocsMiddleware extends QueryLogger
1515
private array $queries = [];
1616
private array $logs = [];
1717
private array $models = [];
18+
private array $modelsTimeline = [];
1819

1920
public function handle($request, Closure $next)
2021
{
@@ -46,6 +47,8 @@ public function handle($request, Closure $next)
4647
'queries' => $this->queries,
4748
'logs' => $this->logs,
4849
'models' => $this->models,
50+
// 'modelsTimeline' => $this->modelsTimeline,
51+
'modelsTimeline' => array_unique($this->modelsTimeline, SORT_REGULAR),
4952
'memory' => (string) round(memory_get_peak_usage(true) / 1048576, 2) . "MB",
5053
];
5154
$jsonContent = json_encode($content);
@@ -82,6 +85,7 @@ public function listenToModels()
8285
foreach (array_filter($models) as $model) {
8386
// doing and booted ignore
8487
if (Str::startsWith($event, 'eloquent.booting')
88+
|| Str::startsWith($event, 'eloquent.booted')
8589
|| Str::startsWith($event, 'eloquent.retrieving')
8690
|| Str::startsWith($event, 'eloquent.creating')
8791
|| Str::startsWith($event, 'eloquent.saving')
@@ -95,6 +99,11 @@ public function listenToModels()
9599
$event = Str::replace('eloquent.', '', $event);
96100
$class = get_class($model);
97101

102+
$this->modelsTimeline[] = [
103+
'event' => $event,
104+
'model' => $class,
105+
];
106+
98107
if (!isset($this->models[$class])) {
99108
$this->models[$class] = [];
100109
}

ui/src/components/ApiAction.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ export default function ApiAction(props: Props) {
3333
const [responseData, setResponseData] = useState("");
3434
const [sqlQueriesCount, setSqlQueriesCount] = useState(0);
3535
const [sqlData, setSqlData] = useState("");
36-
const [modelsData, setModelsData] = useState({});
36+
const [modelsData, setModelsData] = useState({
37+
modelsSummary: [],
38+
modelsTimeline: []
39+
});
3740
const [logsData, setLogsData] = useState("");
3841
const [serverMemory, setServerMemory] = useState("");
3942
const [responseStatus, setResponseStatus] = useState(0);
@@ -151,8 +154,11 @@ export default function ApiAction(props: Props) {
151154
if (data && data._lrd && data._lrd.memory) {
152155
setServerMemory(data._lrd.memory)
153156
}
154-
if (data && data._lrd && data._lrd.models) {
155-
setModelsData(data._lrd.models)
157+
if (data && data._lrd && data._lrd.models && data._lrd.modelsTimeline) {
158+
setModelsData({
159+
modelsSummary: data._lrd.models,
160+
modelsTimeline: data._lrd.modelsTimeline
161+
})
156162
}
157163
// remove key _lrd from response
158164
if (data && data._lrd) {

ui/src/components/ApiInfo.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,12 @@ export default function ApiInfo(props: Props) {
7474
}
7575
if (keyPart == 'regex') {
7676
return (
77-
<div className="block">
78-
Match: <code>${valuePart}</code>
79-
</div>
77+
<>
78+
<div className="inline-block badge badge-info badge-outline mt-1 mb-1 mr-2 rounded-sm">
79+
Regexp
80+
</div>
81+
<code>${valuePart}</code>
82+
</>
8083
)
8184
}
8285

@@ -112,13 +115,9 @@ export default function ApiInfo(props: Props) {
112115
<span className='text-blue-500 pr-1'>¬</span>
113116
<code className='pl-1'>
114117
{key}
115-
{lrdDocsItem.rules[key].map((rule) => (
116-
rule.split('|').map((theRule) => (
117-
(theRule == "array" || key.endsWith(".*")) ? (
118-
<ChevronRightIcon key={shortid.generate()} className='inline-block w-4 h-4' />
119-
) : (<span key={shortid.generate()}></span>)
120-
))
121-
))}
118+
{(key.endsWith(".*")) ? (
119+
<ChevronRightIcon key={shortid.generate()} className='inline-block w-4 h-4' />
120+
) : (<span key={shortid.generate()}></span>)}
122121
</code>
123122
{lrdDocsItem.rules[key].map((rule) => (
124123
rule.split('|').map((theRule) => (

ui/src/components/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ export default function App() {
6565
response
6666
.then(lrdDocsJson => lrdDocsJson.json())
6767
.then((lrdDocsJson) => {
68+
// check if not an array
69+
if (!Array.isArray(lrdDocsJson)) {
70+
setError("Invalid response")
71+
setSendingRequest(false)
72+
return
73+
}
6874
setError(null)
6975
setLrdDocsJson(lrdDocsJson)
7076
setLrdDocsJsonCopy(lrdDocsJson)
Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,46 @@
11
import React from 'react';
22

33
import { explode } from '../../libs/strings'
4+
import shortid from 'shortid';
45

56
interface Props {
67
modelsData: any,
78
}
89

910
export default function ApiActionEvents(props: Props) {
1011
const { modelsData } = props
12+
const { modelsSummary, modelsTimeline } = modelsData
1113

1214
return (
1315
<>
14-
{!Object.keys(modelsData).length && (
16+
{!Object.keys(modelsSummary).length && (
1517
<div className='text-center text-sm text-slate-500'>
1618
No Models Data
1719
</div>
1820
)}
19-
{Object.keys(modelsData).length != 0 && (
21+
{Object.keys(modelsSummary).length != 0 && (
2022
<>
21-
<h3 className='title'>Models</h3>
22-
<p>
23-
<small className='text-slate-500'>Events are in the order of occurances</small>
24-
</p>
23+
<h3 className='title'>Model events Summary</h3>
2524
<div className='divider'></div>
26-
{Object.keys(modelsData).map((model, index) => {
25+
{Object.keys(modelsSummary).map((model) => {
2726
return (
28-
<table className='table table-compact table-fixed table-zebra w-full mb-10' key={index}>
27+
<table className='table table-compact table-fixed table-zebra w-full mb-10' key={shortid.generate()}>
2928
<tbody>
30-
{Object.keys(modelsData[model]).map((event, idx) => {
29+
{Object.keys(modelsSummary[model]).map((event, idx) => {
3130
return (
32-
<tr key={idx}>
31+
<tr key={shortid.generate()}>
3332
{idx == 0 && (
34-
<td rowSpan={Object.keys(modelsData[model]).length}>
33+
<td rowSpan={Object.keys(modelsSummary[model]).length}>
3534
<span className='font-bold text-slate-500'>Model</span>
3635
<br />
3736
<div className='' dangerouslySetInnerHTML={{ __html: explode(model.split('\\')[model.split('\\').length - 1], 30, "<br/>") }} />
3837
</td>
3938
)}
4039
<td className='capitalize'>{event}</td>
4140
<td>
42-
<span className='font-bold'>{modelsData[model][event]}</span>
41+
<span className='font-bold'>{modelsSummary[model][event]}</span>
4342
<span className='text-slate-400 pl-1'>
44-
Time{modelsData[model][event] > 1 ? 's' : ''}
43+
Time{modelsSummary[model][event] > 1 ? 's' : ''}
4544
</span>
4645
</td>
4746
</tr>
@@ -53,6 +52,40 @@ export default function ApiActionEvents(props: Props) {
5352
})}
5453
</>
5554
)}
55+
56+
{Object.keys(modelsTimeline).length != 0 && (
57+
<>
58+
<h3 className='title'>Model Events Timeline</h3>
59+
<small className='text-slate-500'>Events are in the order of occurrences</small>
60+
<div className='divider'></div>
61+
<div className="flex flex-col md:grid grid-cols-12">
62+
{Object.keys(modelsTimeline).map((index) => {
63+
return (
64+
<div className="flex md:contents" key={shortid.generate()}>
65+
<div className="col-start-2 col-end-4 mr-10 md:mx-auto relative">
66+
<div className="h-full w-3 flex items-center justify-center">
67+
<div className={`h-full w-1 bg-${modelsTimeline[index].event} pointer-events-none`}></div>
68+
</div>
69+
<div className={`w-3 h-3 absolute top-1/2 rounded-full bg-${modelsTimeline[index].event} shadow text-center`}>
70+
<i className="fas fa-check-circle text-white"></i>
71+
</div>
72+
</div>
73+
<div className="col-start-4 col-end-12 rounded-md my-3 mr-auto w-full break-all">
74+
<h3 className="mb-1">
75+
<div className='' dangerouslySetInnerHTML={{ __html: explode(modelsTimeline[index].model.split('\\')[modelsTimeline[index].model.split('\\').length - 1], 30, "<br/>") }} />
76+
</h3>
77+
<p className="title text-justify w-full">
78+
<span className={`badge badge-${modelsTimeline[index].event} rounded-sm`}>
79+
{ modelsTimeline[index].event}
80+
</span>
81+
</p>
82+
</div>
83+
</div>
84+
)
85+
})}
86+
</div>
87+
</>
88+
)}
5689
</>
5790
)
5891
}

ui/src/components/elements/ApiActionInfo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default function ApiActionInfo(props: Props) {
7272
<ChevronRightIcon className='inline-block w-4 h-4 ml-1' />
7373
</div>
7474
<div className="collapse-content p-0">
75-
{lrdDocsItem.responses.map((response) => (
75+
{lrdDocsItem.responses && lrdDocsItem.responses.map((response) => (
7676
<div key={shortid.generate()}>
7777
<div className={`response response-${response}`}>
7878
- {response} &nbsp; {responsesText[response]}

ui/src/components/elements/ApiActionRequest.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,15 @@ export default function ApiActionRequest(props: Props) {
9595
<div className="collapse-title text-sm text-slate-500 pl-0">
9696
Set Global Headers
9797
</div>
98-
<div className='text-sm text-slate-500 p-0'>
99-
<ExclamationTriangleIcon className='inline-block w-4 h-4 ml-1 text-yellow-500' />
100-
&nbsp; This request requires a file upload. <br />
101-
<LockOpenIcon className='inline-block w-4 h-4 ml-1 text-slate-500' />
102-
&nbsp; Global headers will be overridden as <code>application/json</code><code>multipart/form-data</code>
103-
<br />
104-
</div>
98+
{(files.length != 0 && (method == 'POST' || method == 'PUT' || method == 'DELETE')) && (
99+
<div className='text-sm text-slate-500 p-0'>
100+
<ExclamationTriangleIcon className='inline-block w-4 h-4 ml-1 text-yellow-500' />
101+
&nbsp; This request requires a file upload. <br />
102+
<LockOpenIcon className='inline-block w-4 h-4 ml-1 text-slate-500' />
103+
&nbsp; Global headers will be overridden as <code>application/json</code><code>multipart/form-data</code>
104+
<br />
105+
</div>
106+
)}
105107
<div className="collapse-content p-0">
106108
<AceEditor
107109
height='200px'

ui/src/global.css

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,42 @@ td {
7171
.method-HEAD {
7272
@apply text-info;
7373
}
74-
.badge-200,.badge-201,.badge-202,.response-204 {
74+
.bg-created {
7575
@apply bg-success;
7676
}
77-
.badge-422,.badge-429 {
77+
.bg-saved {
78+
@apply bg-info;
79+
}
80+
.bg-updated {
81+
@apply bg-warning;
82+
}
83+
.bg-deleted {
84+
@apply bg-error;
85+
}
86+
.bg-retrieved {
87+
@apply bg-gray-800;
88+
}
89+
.badge-created {
90+
@apply badge-success;
91+
}
92+
.badge-saved {
93+
@apply badge-info;
94+
}
95+
.badge-updated {
96+
@apply badge-warning;
97+
}
98+
.badge-deleted {
99+
@apply badge-error;
100+
}
101+
.badge-retrieved {
102+
@apply badge-ghost;
103+
}
104+
105+
.badge-200,.badge-201,.badge-202,.response-204, {
106+
@apply bg-success;
107+
}
108+
109+
.badge-422,.badge-429, {
78110
@apply bg-warning;
79111
}
80112
.badge-400,.badge-401,.badge-402,.badge-403,.badge-404,.badge-405 {

0 commit comments

Comments
 (0)