Merge commit 'a087961fab0d73cc54f9cae658e9eb50ab060f96'

This commit is contained in:
Jon Herron 2022-10-13 20:45:09 -04:00
commit 3803b835c2
8 changed files with 133 additions and 25 deletions

View File

@ -43,6 +43,7 @@
"react-bootstrap-typeahead": "^6.0.0", "react-bootstrap-typeahead": "^6.0.0",
"react-datepicker": "^4.8.0", "react-datepicker": "^4.8.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-icons": "^4.4.0",
"react-jsonschema-form": "^1.8.1", "react-jsonschema-form": "^1.8.1",
"react-markdown": "^8.0.3", "react-markdown": "^8.0.3",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
@ -22404,6 +22405,14 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
}, },
"node_modules/react-icons": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz",
"integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "16.9.0", "version": "16.9.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
@ -44975,7 +44984,7 @@
"@csstools/postcss-text-decoration-shorthand": "^1.0.0", "@csstools/postcss-text-decoration-shorthand": "^1.0.0",
"@csstools/postcss-trigonometric-functions": "^1.0.2", "@csstools/postcss-trigonometric-functions": "^1.0.2",
"@csstools/postcss-unset-value": "^1.0.2", "@csstools/postcss-unset-value": "^1.0.2",
"autoprefixer": "10.4.8", "autoprefixer": "10.4.5",
"browserslist": "^4.21.3", "browserslist": "^4.21.3",
"css-blank-pseudo": "^3.0.3", "css-blank-pseudo": "^3.0.3",
"css-has-pseudo": "^3.0.4", "css-has-pseudo": "^3.0.4",
@ -45013,7 +45022,8 @@
}, },
"dependencies": { "dependencies": {
"autoprefixer": { "autoprefixer": {
"version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz", "version": "10.4.5",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==", "integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
"requires": { "requires": {
"browserslist": "^4.20.2", "browserslist": "^4.20.2",
@ -45721,6 +45731,12 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
}, },
"react-icons": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz",
"integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==",
"requires": {}
},
"react-is": { "react-is": {
"version": "16.9.0", "version": "16.9.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",

View File

@ -38,6 +38,7 @@
"react-bootstrap-typeahead": "^6.0.0", "react-bootstrap-typeahead": "^6.0.0",
"react-datepicker": "^4.8.0", "react-datepicker": "^4.8.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-icons": "^4.4.0",
"react-jsonschema-form": "^1.8.1", "react-jsonschema-form": "^1.8.1",
"react-markdown": "^8.0.3", "react-markdown": "^8.0.3",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",

View File

@ -1,7 +1,8 @@
export interface Secret { export interface Secret {
id: number;
key: string; key: string;
value: string; value: string;
username: string; creator_user_id: string;
} }
export interface RecentProcessModel { export interface RecentProcessModel {

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import { Button, Table } from 'react-bootstrap'; import { Button, Table } from 'react-bootstrap';
import { MdDelete } from 'react-icons/md';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { getPageInfoFromSearchParams } from '../helpers'; import { getPageInfoFromSearchParams } from '../helpers';
@ -23,17 +24,36 @@ export default function SecretList() {
}); });
}, [searchParams]); }, [searchParams]);
const reloadSecrets = (_result: any) => {
window.location.reload();
};
const handleDeleteSecret = (key: any) => {
HttpService.makeCallToBackend({
path: `/secrets/${key}`,
successCallback: reloadSecrets,
httpMethod: 'DELETE',
});
};
const buildTable = () => { const buildTable = () => {
const rows = secrets.map((row) => { const rows = secrets.map((row) => {
return ( return (
<tr key={(row as any).key}> <tr key={(row as any).key}>
<td>
<Link to={`/admin/secrets/${(row as any).key}`}>
{(row as any).id}
</Link>
</td>
<td> <td>
<Link to={`/admin/secrets/${(row as any).key}`}> <Link to={`/admin/secrets/${(row as any).key}`}>
{(row as any).key} {(row as any).key}
</Link> </Link>
</td> </td>
<td>{(row as any).value}</td>
<td>{(row as any).username}</td> <td>{(row as any).username}</td>
<td>
<MdDelete onClick={() => handleDeleteSecret((row as any).key)} />
</td>
</tr> </tr>
); );
}); });
@ -41,9 +61,10 @@ export default function SecretList() {
<Table striped bordered> <Table striped bordered>
<thead> <thead>
<tr> <tr>
<th>ID</th>
<th>Secret Key</th> <th>Secret Key</th>
<th>Secret Value</th>
<th>Creator</th> <th>Creator</th>
<th>Delete</th>
</tr> </tr>
</thead> </thead>
<tbody>{rows}</tbody> <tbody>{rows}</tbody>
@ -72,12 +93,11 @@ export default function SecretList() {
if (pagination) { if (pagination) {
return ( return (
<> <div>
<Button href="/admin/secrets/new">Add a secret</Button> <h2>Secrets</h2>
<br />
<br />
{SecretsDisplayArea()} {SecretsDisplayArea()}
</> <Button href="/admin/secrets/new">Add a secret</Button>
</div>
); );
} }
return null; return null;

View File

@ -1,5 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Stack } from 'react-bootstrap';
import Button from 'react-bootstrap/Button'; import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form'; import Form from 'react-bootstrap/Form';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -13,6 +14,18 @@ export default function SecretNew() {
navigate(`/admin/secrets/${key}`); navigate(`/admin/secrets/${key}`);
}; };
const navigateToSecrets = () => {
navigate(`/admin/secrets`);
};
const changeSpacesToDash = (someString: string) => {
// change spaces to `-`
let s1 = someString.replace(' ', '-');
// remove any trailing `-`
s1 = s1.replace(/-$/, '');
return s1;
};
const addSecret = (event: any) => { const addSecret = (event: any) => {
event.preventDefault(); event.preventDefault();
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
@ -26,16 +39,22 @@ export default function SecretNew() {
}); });
}; };
const warningStyle = {
color: 'red',
};
return ( return (
<main style={{ padding: '1rem 0' }}> <main style={{ padding: '1rem 0' }}>
<h2>Add Secret</h2> <h2>Add Secret</h2>
<Form onSubmit={addSecret}> <Form onSubmit={addSecret}>
<Form.Group className="mb-3" controlId="formDisplayName"> <Form.Group className="mb-3" controlId="formDisplayName">
<Form.Label>Key:</Form.Label> <Form.Label>
Key: <span style={warningStyle}>No Spaces</span>
</Form.Label>
<Form.Control <Form.Control
type="text" type="text"
value={key} value={key}
onChange={(e) => setKey(e.target.value)} onChange={(e) => setKey(changeSpacesToDash(e.target.value))}
/> />
</Form.Group> </Form.Group>
<Form.Group className="mb-3" controlId="formIdentifier"> <Form.Group className="mb-3" controlId="formIdentifier">
@ -48,9 +67,14 @@ export default function SecretNew() {
}} }}
/> />
</Form.Group> </Form.Group>
<Button variant="primary" type="submit"> <Stack direction="horizontal" gap={3}>
Submit <Button variant="primary" type="submit">
</Button> Submit
</Button>
<Button variant="danger" type="button" onClick={navigateToSecrets}>
Cancel
</Button>
</Stack>
</Form> </Form>
</main> </main>
); );

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { Stack, Table } from 'react-bootstrap'; import { Stack, Table, Button } from 'react-bootstrap';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { Secret } from '../interfaces'; import { Secret } from '../interfaces';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
@ -10,10 +10,7 @@ export default function SecretShow() {
const params = useParams(); const params = useParams();
const [secret, setSecret] = useState<Secret | null>(null); const [secret, setSecret] = useState<Secret | null>(null);
const [secretValue, setSecretValue] = useState(secret?.value);
const navigateToSecrets = (_result: any) => {
navigate(`/admin/secrets`);
};
useEffect(() => { useEffect(() => {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
@ -22,6 +19,37 @@ export default function SecretShow() {
}); });
}, [params]); }, [params]);
const handleSecretValueChange = (event: any) => {
if (secret) {
setSecretValue(event.target.value);
}
};
// const reloadSecret = (_result: any) => {
// window.location.reload();
// };
const updateSecretValue = () => {
if (secret && secretValue) {
secret.value = secretValue;
HttpService.makeCallToBackend({
path: `/secrets/${secret.key}`,
successCallback: () => {
setSecret(secret);
},
httpMethod: 'PUT',
postBody: {
value: secretValue,
creator_user_id: secret.creator_user_id,
},
});
}
};
const navigateToSecrets = (_result: any) => {
navigate(`/admin/secrets`);
};
const deleteSecret = () => { const deleteSecret = () => {
if (secret === null) { if (secret === null) {
return; return;
@ -34,17 +62,18 @@ export default function SecretShow() {
}; };
if (secret) { if (secret) {
const secretToUse = secret as any;
return ( return (
<> <>
<Stack direction="horizontal" gap={3}> <Stack direction="horizontal" gap={3}>
<h2>Secret Key: {secretToUse.key}</h2> <h2>Secret Key: {secret.key}</h2>
<ButtonWithConfirmation <ButtonWithConfirmation
description="Delete Secret?" description="Delete Secret?"
onConfirmation={deleteSecret} onConfirmation={deleteSecret}
buttonLabel="Delete" buttonLabel="Delete"
/> />
<Button variant="warning" onClick={updateSecretValue}>
Update Value
</Button>
</Stack> </Stack>
<div> <div>
<Table striped bordered> <Table striped bordered>
@ -57,7 +86,15 @@ export default function SecretShow() {
<tbody> <tbody>
<tr> <tr>
<td>{params.key}</td> <td>{params.key}</td>
<td>{secretToUse.value}</td> <td>
<input
id="secret_value"
name="secret_value"
type="text"
value={secretValue || secret.value}
onChange={handleSecretValueChange}
/>
</td>
</tr> </tr>
</tbody> </tbody>
</Table> </Table>

View File

@ -17,7 +17,6 @@ export default function TaskShow() {
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorMessage = (useContext as any)(ErrorContext)[1];
useEffect(() => { useEffect(() => {
setErrorMessage('');
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/tasks/${params.process_instance_id}/${params.task_id}`, path: `/tasks/${params.process_instance_id}/${params.task_id}`,
successCallback: setTask, successCallback: setTask,

View File

@ -67,11 +67,15 @@ backendCallProps) => {
}); });
let isSuccessful = true; let isSuccessful = true;
let is403 = false;
fetch(`${BACKEND_BASE_URL}${path}`, httpArgs) fetch(`${BACKEND_BASE_URL}${path}`, httpArgs)
.then((response) => { .then((response) => {
if (response.status === 401) { if (response.status === 401) {
UserService.doLogin(); UserService.doLogin();
throw new UnauthenticatedError('You must be authenticated to do this.'); throw new UnauthenticatedError('You must be authenticated to do this.');
} else if (response.status === 403) {
is403 = true;
isSuccessful = false;
} else if (!response.ok) { } else if (!response.ok) {
isSuccessful = false; isSuccessful = false;
} }
@ -80,6 +84,10 @@ backendCallProps) => {
.then((result: any) => { .then((result: any) => {
if (isSuccessful) { if (isSuccessful) {
successCallback(result); successCallback(result);
} else if (is403) {
// Hopefully we can make this service a hook and use the error message context directly
// eslint-disable-next-line no-alert
alert(result.message);
} else { } else {
let message = 'A server error occurred.'; let message = 'A server error occurred.';
if (result.message) { if (result.message) {
@ -89,6 +97,8 @@ backendCallProps) => {
failureCallback(message); failureCallback(message);
} else { } else {
console.error(message); console.error(message);
// eslint-disable-next-line no-alert
alert(message);
} }
} }
}) })