Skip to content

Commit

Permalink
Merge branch 'kallewesterling/issue393' into improve-api-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
kallewesterling committed Feb 8, 2024
2 parents d6f0d34 + ab4c80c commit d7bb726
Show file tree
Hide file tree
Showing 71 changed files with 2,517 additions and 590 deletions.
49 changes: 0 additions & 49 deletions HOWTO_reset_the_database.md

This file was deleted.

18 changes: 16 additions & 2 deletions eap_backend/eap_api/view_utils.py
Expand Up @@ -26,13 +26,24 @@
"serializer": AssuranceCaseSerializer,
"model": AssuranceCase,
"children": ["goals"],
"fields": ("name", "description", "lock_uuid", "owner", "color_profile"),
"fields": (
"name",
"description",
"lock_uuid",
"owner",
"color_profile",
),
},
"goal": {
"serializer": TopLevelNormativeGoalSerializer,
"model": TopLevelNormativeGoal,
"children": ["context", "property_claims", "strategies"],
"fields": ("name", "short_description", "long_description", "keywords"),
"fields": (
"name",
"short_description",
"long_description",
"keywords",
),
"parent_types": [("assurance_case", False)],
},
"context": {
Expand Down Expand Up @@ -259,6 +270,9 @@ def get_case_permissions(case, user):
"view": if user is a member of a group that has view rights on the case
None otherwise.
"""
if isinstance(case, (int, str)):
case = AssuranceCase.objects.get(pk=int(case))

if (not case.owner) or (case.owner == user):
# case has no owner - anyone can view it, or user is owner
return "manage"
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/components/CaseAccessibilityModal.jsx
Expand Up @@ -18,6 +18,23 @@ import configData from "../config.json";
import { Wheelchair } from "./common/Icons.jsx";
import ErrorMessage from "./common/ErrorMessage.jsx";

/**
* CaseAccessibilityModal provides a modal dialog for editing the accessibility options,
* specifically the colour profile, of an assurance case diagram. Users can select from
* predefined colour schemes configured in `config.json` to enhance diagram accessibility.
*
* @param {Object} props The component props.
* @param {boolean} props.isOpen Indicates if the modal is open.
* @param {Function} props.onClose Function to call when closing the modal.
* @param {Function} props.onSuccess Function to call upon successful update of the case.
* @param {string} props.caseId The ID of the case being edited.
* @param {string} props.currentColour The current colour profile of the case diagram.
*
* This component utilizes the `editCase` API to submit the selected colour profile update,
* handling loading states, success, and error feedback within the modal dialog. The colour
* selection is made through a radio button group, offering a user-friendly way to enhance
* diagram accessibility for users with visual impairments.
*/
function CaseAccessibilityModal({
isOpen,
onClose,
Expand Down
36 changes: 29 additions & 7 deletions frontend/src/components/CaseContainer.js
Expand Up @@ -15,6 +15,14 @@ import { ColumnFlow, RowFlow } from "./common/Layout.jsx";
import { Add, Subtract, Target } from "./common/Icons.jsx";
import ErrorMessage from "./common/ErrorMessage.jsx";

/**
* CaseContainer serves as the main component for displaying and interacting with an assurance case.
* It integrates various components such as MermaidChart for visual representation, ItemEditor for editing case items,
* and CaseTopBar for additional case management functions. This component handles loading of case data,
* user authentication, and provides zoom and pan functionality for the case diagram.
*
* @returns {JSX.Element} Renders the assurance case container with editing capabilities and visualization controls.
*/
function CaseContainer() {
const { caseSlug } = useParams();
const theme = useTheme();
Expand Down Expand Up @@ -120,13 +128,16 @@ function CaseContainer() {
}
}, [assuranceCase]);

/**
* Generates a unique identifier for new elements within the assurance case based on the type and its parents.
* It prefixes the identifier based on the type and ensures uniqueness within the case.
*
* @param {string} type - The type of the element for which the ID is being generated.
* @param {string} parentId - The ID of the parent element.
* @param {string} parentType - The type of the parent element.
* @returns {string} A unique identifier for the new element.
*/
const getIdForNewElement = useCallback(
/**
* @param {string} type
* @param {string} parentId
* @param {string} parentType
* @returns {string}
*/
(type, parentId, parentType) => {
let prefix = configData.navigation[type].db_name
.substring(0, 1)
Expand Down Expand Up @@ -154,6 +165,11 @@ function CaseContainer() {
[assuranceCase, identifiers],
);

/**
* Updates all identifiers within the assurance case to ensure they are unique.
* This function might be necessary when there are changes to the case structure
* or to correct any identifier conflicts.
*/
const updateAllIdentifiers = useCallback(() => {
setIsLoading(true);

Expand Down Expand Up @@ -381,7 +397,13 @@ function CaseContainer() {
);
}

/** @returns {string[]} */
/**
* Generates a list of identifiers from the assurance case. It recursively visits
* each item in the case structure, collecting the names to form a set of identifiers.
*
* @param {Object} assuranceCase - The assurance case object from which to generate the list.
* @returns {string[]} A list of unique identifiers derived from the assurance case items.
*/
function updateIdList(assuranceCase) {
const set = [];
assuranceCase.goals.forEach((goal) => {
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/components/CaseCreator.js
Expand Up @@ -6,6 +6,18 @@ import CaseImporterFlow from "./CaseImporterFlow.jsx";
import ModalDialog from "./common/ModalDialog.jsx";
import useId from "@mui/utils/useId";

/**
* CaseCreator component that toggles between the CaseCreatorFlow and CaseImporterFlow based on user action.
* It presents a modal dialog which either guides the user through creating a new assurance case
* or importing an existing one. The component ensures that a user is logged in before allowing case creation
* or importation.
*
* @param {Object} props - Component props.
* @param {boolean} props.isOpen - Determines if the modal dialog is open.
* @param {Function} props.onClose - Handler called when the modal dialog is requested to close.
* @param {boolean} props.isImport - Flag determining which flow to show: true for import, false for creation.
* @returns {JSX.Element|null} The rendered component if the user is logged in, otherwise null.
*/
function CaseCreator({ isOpen, onClose, isImport }) {
const titleId = useId();

Expand Down
15 changes: 14 additions & 1 deletion frontend/src/components/CaseCreatorFlow.jsx
Expand Up @@ -10,10 +10,23 @@ import LoadingSpinner from "./common/LoadingSpinner.jsx";
import { ArrowRight } from "./common/Icons.jsx";
import ErrorMessage from "./common/ErrorMessage.jsx";

// see models.py
/**
* Maximum allowed lengths for title and description,
* see /eap_backend/eap_api/models.py
*/
const titleMaxLength = 200;
const descriptionMaxLength = 1000;


/**
* CaseCreatorFlow component guides the user through the process of creating a new assurance case.
* It consists of two stages: entering basic case details (title and description),
* and selecting a template for the case. Upon completion, the case is posted to the server.
*
* @param {Object} props Component props.
* @param {string} props.titleId A unique ID for the title element, used for accessibility.
* @param {Function} props.onClose Function to call when the user chooses to close the modal.
*/
function CaseCreatorFlow({ titleId, onClose }) {
const [stage, setStage] = useState(0);

Expand Down
45 changes: 44 additions & 1 deletion frontend/src/components/CaseImporterFlow.jsx
Expand Up @@ -17,6 +17,14 @@ import FileInput from "./common/FileInput.jsx";
import { ArrowRight } from "./common/Icons.jsx";
import ErrorMessage from "./common/ErrorMessage.jsx";

/**
* CaseImporterFlow allows users to import an assurance case into the TEA Platform from either a file or a URL.
* It supports importing from SVG files with embedded JSON metadata or directly from JSON files.
*
* @param {Object} props The component props.
* @param {string} props.titleId A unique identifier for the title element, used for accessibility.
* @param {Function} props.onClose A function to call when closing the import modal.
*/
function CaseImporterFlow({ titleId, onClose }) {
const [uploadType, setUploadType] = useState("file");
const [url, setUrl] = useState("");
Expand All @@ -33,6 +41,11 @@ function CaseImporterFlow({ titleId, onClose }) {
const baseURL = `${getBaseURL()}`;
const navigate = useNavigate();

/** Parses SVG text to extract embedded JSON metadata
* @param {string} text The SVG text to parse
* @returns {Promise<Object>} The parsed JSON metadata
* @throws {Error} If the metadata cannot be parsed
*/
const parseSvg = useCallback(async (text) => {
const parser = new DOMParser();
const svgDoc = parser.parseFromString(text, "image/svg+xml");
Expand All @@ -48,8 +61,13 @@ function CaseImporterFlow({ titleId, onClose }) {
}
}, []);

/** Fetches content from a URL and tries to parse it as JSON or SVG
*
* @param {string} url
* @returns {Promise<Object>} The parsed JSON or SVG content
* @throws {Error} If the content cannot be loaded or parsed
*/
const getUrlContent = useCallback(
/** @param {string} url */
async (url) => {
try {
const response = await fetch(url);
Expand All @@ -75,6 +93,13 @@ function CaseImporterFlow({ titleId, onClose }) {
[parseSvg]
);

/**
* Posts the JSON representation of a case to the backend
*
* @param {Object} json The case JSON to post
* @returns {Promise<void>}
* @throws {Error} If the JSON cannot be posted
*/
const postCaseJSON = useCallback(
(json) => {
const requestOptions = {
Expand Down Expand Up @@ -108,6 +133,13 @@ function CaseImporterFlow({ titleId, onClose }) {
[token, baseURL, navigate]
);

/**
* Handles form submission for importing a case
*
* @param {Event} e The form submission event
* @returns {void}
* @throws {Error} If the form submission fails
*/
const onSubmit = useCallback(
(e) => {
e.preventDefault();
Expand All @@ -133,10 +165,21 @@ function CaseImporterFlow({ titleId, onClose }) {
[uploadType, url, fileJson, getUrlContent, postCaseJSON]
);

/**
* Handles changes in the selected upload type (file or URL)
* @param {Event} e The change event
* @returns {void}
* @throws {Error} If the change event fails
*/
const onTypeChange = useCallback((e) => {
setUploadType(e.target.value);
}, []);

/**
* Processes the selected file to extract JSON data.
*
* @returns {void}
*/
useEffect(() => {
if (!file) {
setFileJson(null);
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/components/CaseMediaPreview.jsx
Expand Up @@ -9,12 +9,29 @@ import { getCase } from "./caseApi";
import { Box } from "@mui/material";
import { LoadingCard } from "./ManageCases";

/**
* CaseMediaPreview dynamically renders a visual preview of an assurance case.
* Depending on the configuration, it either displays a Mermaid chart representation of the case
* or a static mockup image. This component is intended to enhance the user interface by providing
* a quick glance at the case structure or content.
*
* @param {Object} props - Component props.
* @param {Object} props.caseObj - The case object containing information such as the case ID.
* @returns {JSX.Element} A visual representation of the assurance case.
*/
export const CaseMediaPreview = ({ caseObj }) => {
const theme = useTheme();
const [token] = useLoginToken();
const [assuranceCase, setAssuranceCase] = useState();
const [isLoading, setIsLoading] = useState(true);

/**
* Fetch the detailed assurance case data when the component mounts.
*
* @param {string} token - The user's login token.
* @param {Object} caseObj - The case object containing information such as the case ID.
* @returns {void}
*/
useEffect(() => {
if (token) {
let isMounted = true;
Expand All @@ -31,12 +48,14 @@ export const CaseMediaPreview = ({ caseObj }) => {
// TODO show error to user
});

// Cleanup function to handle component unmount
return () => {
isMounted = false;
};
}
}, [token, caseObj]);

// Conditionally render the Mermaid chart or a static image based on configuration
if (configData.use_case_preview_svg) {
return (
<>
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/components/CasePermissionsManager.js
Expand Up @@ -12,6 +12,17 @@ import { getCase } from "./caseApi.js";
import { useLoginToken } from "../hooks/useAuth.js";
import ErrorMessage from "./common/ErrorMessage.jsx";

/**
* CasePermissionsManager serves as a modal dialog for managing group permissions for a specific assurance case within the TEA Platform. It provides an interface for viewing and editing the access levels (view or edit) of various groups associated with the case.
*
* @param {Object} props - Component props.
* @param {string} props.caseId - The unique identifier of the assurance case.
* @param {Object} props.assuranceCase - The assurance case object.
* @param {boolean} props.isOpen - Controls the visibility of the modal dialog.
* @param {Function} props.onClose - Callback function that is called when the modal is requested to be closed.
* @param {Function} props.onSuccess - Callback function that is called after successfully updating permissions.
* @returns {JSX.Element} A modal dialog for managing group permissions.
*/
function CasePermissionsManager({
caseId,
assuranceCase,
Expand All @@ -38,6 +49,16 @@ function CasePermissionsManager({
);
}

/**
* CasePermissionsManagerInner is the core component within the CasePermissionsManager modal dialog, responsible for the actual management of group permissions. It allows users to assign 'view' or 'edit' permissions to groups for the specified assurance case, or remove their access entirely.
*
* @param {Object} props - Component props.
* @param {string} props.caseId - The unique identifier of the assurance case for which permissions are being managed.
* @param {Object} props.assuranceCase - The assurance case object, used for preloading existing permissions.
* @param {Function} props.afterSubmit - Callback function that is called after permissions are successfully updated.
* @param {Function} props.onClose - Callback function to close the permissions manager.
* @returns {JSX.Element} The interface for managing group permissions, including a list of groups with selectable permissions and action buttons to submit changes or cancel the operation.
*/
function CasePermissionsManagerInner({
caseId,
assuranceCase,
Expand Down

0 comments on commit d7bb726

Please sign in to comment.