Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Website: Add error report modal #1102

Merged
merged 50 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b100814
Collect request errors
bgrgicak Mar 8, 2024
5b03727
Remove service worker logs
bgrgicak Mar 8, 2024
16295b6
Add fatal error event
bgrgicak Mar 8, 2024
bc874a5
Fix base-php error
bgrgicak Mar 8, 2024
db6bde2
Add event listener
bgrgicak Mar 8, 2024
0c1ac32
Fix linter errors
bgrgicak Mar 8, 2024
20d5190
Remove debug code
bgrgicak Mar 8, 2024
a9dd3a9
Use custom event target instead of window
bgrgicak Mar 12, 2024
669757e
Collect request errors
bgrgicak Mar 8, 2024
da44dd0
Remove service worker logs
bgrgicak Mar 8, 2024
f295b8c
Add fatal error event
bgrgicak Mar 8, 2024
24f4d0d
Fix base-php error
bgrgicak Mar 8, 2024
c3d4941
Add event listener
bgrgicak Mar 8, 2024
6520a2b
Fix linter errors
bgrgicak Mar 8, 2024
f447ca9
Remove debug code
bgrgicak Mar 8, 2024
f8694b8
Use custom event target instead of window
bgrgicak Mar 12, 2024
416e40c
Add error report modal
bgrgicak Mar 12, 2024
298e241
Always show fields
bgrgicak Mar 12, 2024
12418ef
Merge branch 'add/logger-crash-event' into add/error-report-modal
bgrgicak Mar 12, 2024
6e5f8e0
Collect request errors
bgrgicak Mar 8, 2024
29d0553
Add fatal error event
bgrgicak Mar 8, 2024
e473349
Remove debug code
bgrgicak Mar 8, 2024
16a9ed1
Merge branch 'add/error-report-modal' of https://github.com/WordPress…
bgrgicak Mar 12, 2024
9762186
Merge branch 'trunk' into add/logger-crash-event
bgrgicak Mar 12, 2024
8891776
Merge branch 'add/logger-crash-event' into add/error-report-modal
bgrgicak Mar 12, 2024
7bb6157
Linter
bgrgicak Mar 12, 2024
93e28c0
Inline dispatch event method
bgrgicak Mar 12, 2024
8f6c9d3
Update packages/playground/website/src/components/error-report-modal/…
bgrgicak Mar 13, 2024
fc3c28b
Update packages/playground/website/src/components/error-report-modal/…
bgrgicak Mar 13, 2024
a7f2347
Remove section
bgrgicak Mar 13, 2024
9c5b595
Merge branch 'add/logger-crash-event' into add/error-report-modal
bgrgicak Mar 13, 2024
be0148c
Merge branch 'trunk' into add/logger-crash-event
bgrgicak Mar 13, 2024
a9aa605
Add event listener tests
bgrgicak Mar 13, 2024
88d497f
Merge branch 'add/logger-crash-event' into add/error-report-modal
bgrgicak Mar 13, 2024
74eecae
Merge branch 'trunk' into add/error-report-modal
bgrgicak Mar 19, 2024
212e72d
Add untested API request and update submit messages
bgrgicak Mar 19, 2024
f46d37b
Merge branch 'trunk' into add/error-report-modal
bgrgicak Mar 21, 2024
65b683f
Allow CORS requests to logger.php
bgrgicak Mar 22, 2024
009bc98
Vif validation issues
bgrgicak Mar 22, 2024
0e0a441
Toggle form states
bgrgicak Mar 22, 2024
22c3fac
Merge branch 'trunk' into add/error-report-modal
bgrgicak Mar 22, 2024
ee0bd6f
Remove temp code
bgrgicak Mar 22, 2024
978dec8
Prevent PHP errors from triggering modal
bgrgicak Mar 22, 2024
ac9f9cd
Fix typo
bgrgicak Mar 26, 2024
8c33769
Update packages/playground/website/src/components/error-report-modal/…
bgrgicak Mar 26, 2024
38e8889
Merge branch 'trunk' into add/error-report-modal
bgrgicak Mar 26, 2024
74f6c7a
Separate log message parts
bgrgicak Mar 26, 2024
c5e1ca5
Format logger
bgrgicak Mar 27, 2024
b2dfafc
Add report error button
bgrgicak Mar 27, 2024
559a603
Merge branch 'trunk' into add/error-report-modal
bgrgicak Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ When Playground crashes on Playground.WordPress.net users are able to submit a c
## Development

Logs are sent to the [logger API on Playground.WordPress.net](https://github.com/WordPress/wordpress-playground/blob/c52d7dbd94dbe3ffc57adde4d9844545ade97f93/packages/playground/website/public/logger.php). The logger API is a simple REST API that accepts a POST request with a `message` parameter.
The API validates the message and then sends it to the [#playground-logs channel on the Making WordPress Slack(https://wordpress.slack.com/archives/C06Q5DCKZ3L).
The API validates the message and then sends it to the [#playground-logs channel on the Making WordPress Slack](https://wordpress.slack.com/archives/C06Q5DCKZ3L).

### Slack app

Expand Down
1 change: 1 addition & 0 deletions packages/php-wasm/logger/src/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export class Logger extends EventTarget {
new CustomEvent(this.fatalErrorEvent, {
detail: {
logs: this.getLogs(),
source: event.source,
},
})
);
Expand Down
4 changes: 4 additions & 0 deletions packages/php-wasm/universal/src/lib/base-php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ export abstract class BasePHP implements IsomorphicLocalPHP {
);
// @ts-ignore
error.output = output;
// @ts-ignore
error.source = 'request';
console.error(error);
throw error;
}
Expand All @@ -296,6 +298,8 @@ export abstract class BasePHP implements IsomorphicLocalPHP {
this.dispatchEvent({
type: 'request.error',
error: e as Error,
// Distinguish between PHP request and PHP-wasm errors
source: (e as any).source ?? 'php-wasm',
});
throw e;
} finally {
Expand Down
1 change: 1 addition & 0 deletions packages/php-wasm/universal/src/lib/universal-php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface PHPRequestEndEvent {
export interface PHPRequestErrorEvent {
type: 'request.error';
error: Error;
source?: 'request' | 'php-wasm';
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/website/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ AddEncoding x-gzip .gz
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
</FilesMatch>
<FilesMatch "index\.js|blueprint-schema\.json|wp-cli.phar">
<FilesMatch "index\.js|blueprint-schema\.json|logger.php|wp-cli.phar">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for Playground integrators?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for localhost, but it's also required if we want to allow an integrator to send error reports.

Header set Access-Control-Allow-Origin "*"
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Expand Down
52 changes: 12 additions & 40 deletions packages/playground/website/public/logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,52 +40,24 @@ function response($ok, $error = null)
die(json_encode($response_data));
}

/**
* Validate the message format
*
* @param string $message - The message to validate
* @return bool - If the message is valid
*/
function validate_message($message)
{
// Validate description. Description is required
preg_match('/(?<=What happened\?\n\n)(.*?)(?=\n\nLogs)/s', $message, $description);
if (empty($description)) {
return false;
}

// Validate logs if exists. Logs need to match the PHP error log format
preg_match('/(?<=Logs\n\n)(.*?)(?=\n\nUrl)/s', $message, $logs);
if (!empty($logs)) {
$logs = $logs[0];
if (preg_match('/\[\d{2}-[A-Za-z]{3}-\d{4} \d{2}:\d{2}:\d{2} UTC\](.*)/s', $logs) !== 1) {
return false;
}
}

// Validate URL if exists
preg_match('/(?<=Url\n\n)(.*)/s', $message, $url);
if (!empty($url)) {
$url = $url[0];
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
return false;
}
}

return true;
}

if (empty($token)) {
response(false, 'No token provided');
}

if (!isset($_POST['message'])) {
response(false, 'No message provided');
if (!isset($_POST['description']) || empty($_POST['description'])) {
response(false, 'No description provided');
}
$text = $_POST['message'];
$text = "What happened?\n\n" . $_POST['description'];

if (!validate_message($text)) {
response(false, 'Invalid message');
if (isset($_POST['logs']) && !empty($_POST['logs'])) {
$text .= "\n\nLogs\n\n" . $_POST['logs'];
}

if (isset($_POST['url'])) {
if (filter_var($_POST['url'], FILTER_VALIDATE_URL) === false) {
response(false, 'Invalid URL');
}
$text .= "\n\nUrl\n\n" . $_POST['url'];
}
$text = urlencode($text);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { useEffect, useState } from 'react';
import Modal from '../modal';
import { addFatalErrorListener, logger } from '@php-wasm/logger';
import { Button, TextareaControl, TextControl } from '@wordpress/components';

import css from './style.module.css';

import { usePlaygroundContext } from '../../playground-context';

export function ErrorReportModal() {
const { showErrorModal, setShowErrorModal } = usePlaygroundContext();
const [loading, setLoading] = useState(false);
const [text, setText] = useState('');
const [logs, setLogs] = useState('');
const [url, setUrl] = useState('');
const [submitted, setSubmitted] = useState(false);
const [submitError, setSubmitError] = useState('');

useEffect(() => {
addFatalErrorListener(logger, (e) => {
const error = e as CustomEvent;
if (error.detail?.source === 'php-wasm') {
setShowErrorModal(true);
}
});
}, [setShowErrorModal]);

useEffect(() => {
resetForm();
if (showErrorModal) {
setLogs(logger.getLogs().join(''));
setUrl(window.location.href);
}
}, [showErrorModal, setShowErrorModal, logs, setLogs]);

function resetForm() {
setText('');
setLogs('');
setUrl('');
}

function resetSubmission() {
setSubmitted(false);
setSubmitError('');
}

function onClose() {
setShowErrorModal(false);
resetForm();
resetSubmission();
}

async function onSubmit() {
setLoading(true);
const formdata = new FormData();
formdata.append('description', text);
if (logs) {
formdata.append('logs', logs);
}
if (url) {
formdata.append('url', url);
}
try {
const response = await fetch(
'https://playground.wordpress.net/logger.php',
{
method: 'POST',
body: formdata,
}
);
setSubmitted(true);

const body = await response.json();
if (!body.ok) {
throw new Error(body.error);
}

setSubmitError('');
resetForm();
} catch (e) {
setSubmitError((e as Error).message);
} finally {
setLoading(false);
}
}

function getTitle() {
if (!submitted) {
return 'Report error';
} else if (submitError) {
return 'Failed to report the error';
} else {
return 'Thank you for reporting the error';
}
}

function getContent() {
if (!submitted) {
return (
<>
Playground crashed because of an error. You can help resolve
the issue by sharing the error details with us.
</>
);
} else if (submitError) {
return (
<>
We were unable to submit the error report. Please try again
or open an{' '}
<a href="https://github.com/WordPress/wordpress-playground/issues/">
issue on GitHub.
</a>
</>
);
} else {
return (
<>
Your report has been submitted to the{' '}
<a href="https://wordpress.slack.com/archives/C06Q5DCKZ3L">
Making WordPress #playground-logs Slack channel
</a>{' '}
and will be reviewed by the team.
</>
);
}
}

/**
* Show the form if the error has not been submitted or if there was an error submitting it.
*
* @return {boolean}
*/
function showForm() {
return !submitted || submitError;
}

return (
<Modal isOpen={showErrorModal} onRequestClose={onClose}>
<header className={css.errorReportModalHeader}>
<h2>{getTitle()}</h2>
<p>{getContent()}</p>
</header>
{showForm() && (
<>
<main>
<TextareaControl
label="What happened?"
help="Describe what caused the error and how can we reproduce it."
value={text}
onChange={setText}
className={css.errorReportModalTextarea}
required={true}
/>
<TextareaControl
label="Logs"
value={logs}
onChange={setLogs}
className={css.errorReportModalTextarea}
/>

<TextControl
label="Url"
value={url}
onChange={setUrl}
/>
</main>
<footer className={css.errorReportModalFooter}>
<Button
variant="primary"
onClick={onSubmit}
isBusy={loading}
disabled={loading || !text}
>
Report error
</Button>
<Button onClick={onClose}>Cancel</Button>
</footer>
</>
)}
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.error-report-modal__header h2 {
font-size: 20px;
}

.error-report-modal__header p {
font-size: 13px;
}

.error-report-modal__textarea textarea {
resize: vertical;
width: 100% !important;
}

.error-report-modal__footer {
margin-top: 20px;
}
.error-report-modal__error {
margin: 16px 0 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@
padding: 15px;
text-align: left;
max-height: 90vh;
overflow: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { MenuItem } from '@wordpress/components';
import { bug } from '@wordpress/icons';

import { usePlaygroundContext } from '../../playground-context';

type Props = { onClose: () => void };
export function ReportError({ onClose }: Props) {
const { setShowErrorModal } = usePlaygroundContext();
return (
<MenuItem
icon={bug}
iconPosition="left"
data-cy="report-error"
aria-label="Report an error in Playground"
onClick={() => {
setShowErrorModal(true);
onClose();
}}
>
Report error
</MenuItem>
);
}
9 changes: 8 additions & 1 deletion packages/playground/website/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { StorageType, StorageTypes } from './types';
import { ResetSiteMenuItem } from './components/toolbar-buttons/reset-site';
import { DownloadAsZipMenuItem } from './components/toolbar-buttons/download-as-zip';
import { RestoreFromZipMenuItem } from './components/toolbar-buttons/restore-from-zip';
import { ReportError } from './components/toolbar-buttons/report-error';
import { resolveBlueprint } from './lib/resolve-blueprint';
import { GithubImportMenuItem } from './components/toolbar-buttons/github-import-menu-item';
import { acquireOAuthTokenIfNeeded } from './github/acquire-oauth-token-if-needed';
Expand All @@ -27,6 +28,7 @@ import { ExportFormValues } from './github/github-export-form/form';
import { joinPaths } from '@php-wasm/util';
import { PlaygroundContext } from './playground-context';
import { collectWindowErrors, logger } from '@php-wasm/logger';
import { ErrorReportModal } from './components/error-report-modal';

collectWindowErrors(logger);

Expand Down Expand Up @@ -80,13 +82,17 @@ if (currentConfiguration.wp === '6.3') {
acquireOAuthTokenIfNeeded();

function Main() {
const [showErrorModal, setShowErrorModal] = useState(false);
const [githubExportFiles, setGithubExportFiles] = useState<any[]>();
const [githubExportValues, setGithubExportValues] = useState<
Partial<ExportFormValues>
>({});

return (
<PlaygroundContext.Provider value={{ storage }}>
<PlaygroundContext.Provider
value={{ storage, showErrorModal, setShowErrorModal }}
>
<ErrorReportModal />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up idea – add a "Report an error" button to trigger the modal manually.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done b2dfafc

<PlaygroundViewport
storage={storage}
displayMode={displayMode}
Expand Down Expand Up @@ -115,6 +121,7 @@ function Main() {
storage={currentConfiguration.storage}
onClose={onClose}
/>
<ReportError onClose={onClose} />
<DownloadAsZipMenuItem onClose={onClose} />
<RestoreFromZipMenuItem onClose={onClose} />
<GithubImportMenuItem onClose={onClose} />
Expand Down
Loading
Loading