diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json
index 765c2790..f6b8687d 100644
--- a/spiffworkflow-frontend/package-lock.json
+++ b/spiffworkflow-frontend/package-lock.json
@@ -43,6 +43,7 @@
"react-bootstrap-typeahead": "^6.0.0",
"react-datepicker": "^4.8.0",
"react-dom": "^18.2.0",
+ "react-icons": "^4.4.0",
"react-jsonschema-form": "^1.8.1",
"react-markdown": "^8.0.3",
"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",
"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": {
"version": "16.9.0",
"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-trigonometric-functions": "^1.0.2",
"@csstools/postcss-unset-value": "^1.0.2",
- "autoprefixer": "10.4.8",
+ "autoprefixer": "10.4.5",
"browserslist": "^4.21.3",
"css-blank-pseudo": "^3.0.3",
"css-has-pseudo": "^3.0.4",
@@ -45013,7 +45022,8 @@
},
"dependencies": {
"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==",
"requires": {
"browserslist": "^4.20.2",
@@ -45721,6 +45731,12 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"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": {
"version": "16.9.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
diff --git a/spiffworkflow-frontend/package.json b/spiffworkflow-frontend/package.json
index 4394c25f..2ce2d7a1 100644
--- a/spiffworkflow-frontend/package.json
+++ b/spiffworkflow-frontend/package.json
@@ -38,6 +38,7 @@
"react-bootstrap-typeahead": "^6.0.0",
"react-datepicker": "^4.8.0",
"react-dom": "^18.2.0",
+ "react-icons": "^4.4.0",
"react-jsonschema-form": "^1.8.1",
"react-markdown": "^8.0.3",
"react-router-dom": "^6.3.0",
diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts
index 2e023c9a..a628940b 100644
--- a/spiffworkflow-frontend/src/interfaces.ts
+++ b/spiffworkflow-frontend/src/interfaces.ts
@@ -1,7 +1,8 @@
export interface Secret {
+ id: number;
key: string;
value: string;
- username: string;
+ creator_user_id: string;
}
export interface RecentProcessModel {
diff --git a/spiffworkflow-frontend/src/routes/SecretList.tsx b/spiffworkflow-frontend/src/routes/SecretList.tsx
index b7a85b19..2b9e756a 100644
--- a/spiffworkflow-frontend/src/routes/SecretList.tsx
+++ b/spiffworkflow-frontend/src/routes/SecretList.tsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import { Button, Table } from 'react-bootstrap';
+import { MdDelete } from 'react-icons/md';
import PaginationForTable from '../components/PaginationForTable';
import HttpService from '../services/HttpService';
import { getPageInfoFromSearchParams } from '../helpers';
@@ -23,17 +24,36 @@ export default function SecretList() {
});
}, [searchParams]);
+ const reloadSecrets = (_result: any) => {
+ window.location.reload();
+ };
+
+ const handleDeleteSecret = (key: any) => {
+ HttpService.makeCallToBackend({
+ path: `/secrets/${key}`,
+ successCallback: reloadSecrets,
+ httpMethod: 'DELETE',
+ });
+ };
+
const buildTable = () => {
const rows = secrets.map((row) => {
return (
+
+
+ {(row as any).id}
+
+ |
{(row as any).key}
|
- {(row as any).value} |
{(row as any).username} |
+
+ handleDeleteSecret((row as any).key)} />
+ |
);
});
@@ -41,9 +61,10 @@ export default function SecretList() {
+ ID |
Secret Key |
- Secret Value |
Creator |
+ Delete |
{rows}
@@ -72,12 +93,11 @@ export default function SecretList() {
if (pagination) {
return (
- <>
-
-
-
+
+
Secrets
{SecretsDisplayArea()}
- >
+
+
);
}
return null;
diff --git a/spiffworkflow-frontend/src/routes/SecretNew.tsx b/spiffworkflow-frontend/src/routes/SecretNew.tsx
index 01c8de5b..7a697a98 100644
--- a/spiffworkflow-frontend/src/routes/SecretNew.tsx
+++ b/spiffworkflow-frontend/src/routes/SecretNew.tsx
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
+import { Stack } from 'react-bootstrap';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import HttpService from '../services/HttpService';
@@ -13,6 +14,18 @@ export default function SecretNew() {
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) => {
event.preventDefault();
HttpService.makeCallToBackend({
@@ -26,16 +39,22 @@ export default function SecretNew() {
});
};
+ const warningStyle = {
+ color: 'red',
+ };
+
return (
Add Secret
- Key:
+
+ Key: No Spaces
+
setKey(e.target.value)}
+ onChange={(e) => setKey(changeSpacesToDash(e.target.value))}
/>
@@ -48,9 +67,14 @@ export default function SecretNew() {
}}
/>
-
+
+
+
+
);
diff --git a/spiffworkflow-frontend/src/routes/SecretShow.tsx b/spiffworkflow-frontend/src/routes/SecretShow.tsx
index b600d01f..707f0d14 100644
--- a/spiffworkflow-frontend/src/routes/SecretShow.tsx
+++ b/spiffworkflow-frontend/src/routes/SecretShow.tsx
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
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 { Secret } from '../interfaces';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
@@ -10,10 +10,7 @@ export default function SecretShow() {
const params = useParams();
const [secret, setSecret] = useState(null);
-
- const navigateToSecrets = (_result: any) => {
- navigate(`/admin/secrets`);
- };
+ const [secretValue, setSecretValue] = useState(secret?.value);
useEffect(() => {
HttpService.makeCallToBackend({
@@ -22,6 +19,37 @@ export default function SecretShow() {
});
}, [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 = () => {
if (secret === null) {
return;
@@ -34,17 +62,18 @@ export default function SecretShow() {
};
if (secret) {
- const secretToUse = secret as any;
-
return (
<>
- Secret Key: {secretToUse.key}
+ Secret Key: {secret.key}
+
diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx
index 7db07d84..21954602 100644
--- a/spiffworkflow-frontend/src/routes/TaskShow.tsx
+++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx
@@ -17,7 +17,6 @@ export default function TaskShow() {
const setErrorMessage = (useContext as any)(ErrorContext)[1];
useEffect(() => {
- setErrorMessage('');
HttpService.makeCallToBackend({
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
successCallback: setTask,
diff --git a/spiffworkflow-frontend/src/services/HttpService.ts b/spiffworkflow-frontend/src/services/HttpService.ts
index bd4c3f93..075956bc 100644
--- a/spiffworkflow-frontend/src/services/HttpService.ts
+++ b/spiffworkflow-frontend/src/services/HttpService.ts
@@ -67,11 +67,15 @@ backendCallProps) => {
});
let isSuccessful = true;
+ let is403 = false;
fetch(`${BACKEND_BASE_URL}${path}`, httpArgs)
.then((response) => {
if (response.status === 401) {
UserService.doLogin();
throw new UnauthenticatedError('You must be authenticated to do this.');
+ } else if (response.status === 403) {
+ is403 = true;
+ isSuccessful = false;
} else if (!response.ok) {
isSuccessful = false;
}
@@ -80,6 +84,10 @@ backendCallProps) => {
.then((result: any) => {
if (isSuccessful) {
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 {
let message = 'A server error occurred.';
if (result.message) {
@@ -89,6 +97,8 @@ backendCallProps) => {
failureCallback(message);
} else {
console.error(message);
+ // eslint-disable-next-line no-alert
+ alert(message);
}
}
})