Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 1 addition & 1 deletion backend/webserver/serializer/project_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Meta:
fields = ["projectId", "projectName", "updated", "deploymentThreshold"]

projectId = serializers.CharField(source="project_id", read_only=True)
projectName = serializers.CharField(source="project_name", allow_blank=True)
projectName = serializers.CharField(source="project_name", allow_blank=True, max_length=255)
Copy link
Member

Choose a reason for hiding this comment

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

How does this length relate to the maximum length of 25 characters declared in backend/analyzer/models.py?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, I haven't seen the constraint in models.py. I think that the check in project_serializer.py is then redundant and can be removed without losing validation. Is that correct? Or should we lave it, but reduce it to 25?

updated = serializers.DateTimeField(default=datetime.now(), read_only=True)
deploymentThreshold = serializers.ChoiceField(source="deployment_threshold", choices=Threshold.names)

Expand Down
3 changes: 3 additions & 0 deletions backend/webserver/views/project_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def post(self, request, project_id):
else:
raise InvalidValueError(project_id)

if len(request.data.get("projectName", "")) > 255:
raise InvalidValueError("Project name exceeds the maximum length of 255 characters")

return Response(f"Creation of {project_id} successful!")

except AlreadyExists as ae:
Expand Down
58 changes: 39 additions & 19 deletions frontend/src/components/NavBar/ProjectCreationContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import {useNotification} from "../../context/NotificationContext";
const CreationContent: React.FunctionComponent<DialogContentProps> = (dialogContentProps: DialogContentProps) => {
const notification = useNotification();
const {data} = useQuery("allProjectsFlat", getAllProjectsFlat)
const [isInvalid, setInvalid] = useState(false);
const [isIdInvalid, setIdInvalid] = useState(true);
const [isNameInvalid, setNameInvalid] = useState(false);
const [isProjectIdTouched, setProjectIdTouched] = useState(false);
const [projectId, setProjectId] = useState("");
const [projectName, setProjectName] = useState("");
const [threshold, setThreshold] = useState("HIGH");
const queryClient = useQueryClient()
const [helperText, setHelperText] = useState("")
const [projectIdHelperText, setProjectIdHelperText] = useState("")
const [projectNameHelperText, setProjectNameHelperText] = useState("")
const handleSave = useMutation(() => createProject(projectId, {
projectName: projectName,
deploymentThreshold: threshold
Expand Down Expand Up @@ -52,42 +55,59 @@ const CreationContent: React.FunctionComponent<DialogContentProps> = (dialogCont
}

useEffect(() => {
if (!isProjectIdTouched) {
return;
}
if (projectId.length < 1){
setInvalid(true);
setHelperText(localization.dialog.projectIdHelperNotEmpty)
setIdInvalid(true);
setProjectIdHelperText(localization.dialog.projectIdHelperNotEmpty)
}else if (projectId.includes(" ")){
setInvalid(true);
setHelperText(localization.dialog.projectIdHelperNoSpaces)
setIdInvalid(true);
setProjectIdHelperText(localization.dialog.projectIdHelperNoSpaces)
}else if (projectId.length > 20){
setInvalid(true);
setHelperText(localization.dialog.projectIdHelperToLong)
setIdInvalid(true);
setProjectIdHelperText(localization.dialog.projectIdHelperToLong)
}else {
if (data?.data !== undefined) {
if (allProjectIds.includes(projectId.toLowerCase())) {
setInvalid(true);
setHelperText(localization.dialog.projectIdHelperIdAlreadyUsed)
setIdInvalid(true);
setProjectIdHelperText(localization.dialog.projectIdHelperIdAlreadyUsed)
} else {
setInvalid(false);
setHelperText("")
setIdInvalid(false);
setProjectIdHelperText("")
}
}
}
}, [projectId])
}, [projectId, isProjectIdTouched, allProjectIds])

useEffect(() => {
if (projectName.length > 255) {
setNameInvalid(true);
setProjectNameHelperText(localization.dialog.projectNameHelperToLong);
} else {
setNameInvalid(false);
setProjectNameHelperText("Optional");
}
}, [projectName]);

return(
<Stack>
<Stack direction={"column"} sx={{width: "20rem", margin: "0px 25px 0px 25px"}}>
<TextField
helperText={helperText}
error={isInvalid}
helperText={projectIdHelperText}
error={isProjectIdTouched && isIdInvalid}
label={localization.dialog.projectId}
value={projectId}
variant="filled"
onChange={(e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => setProjectId(e.target.value)}
onChange={(e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
setProjectId(e.target.value)
setProjectIdTouched(true)
}}
/>
<TextField
style={{marginTop: "1rem"}}
helperText={"Optional"}
helperText={projectNameHelperText}
error={isNameInvalid}
label={localization.dialog.projectName}
value={projectName}
variant="filled"
Expand All @@ -105,7 +125,7 @@ const CreationContent: React.FunctionComponent<DialogContentProps> = (dialogCont
<Button color={"error"}
onClick={() => dialogContentProps.setOpen(false)}>{localization.misc.cancel}</Button>
<Button color={"success"}
disabled={isInvalid}
disabled={isIdInvalid || isNameInvalid}
onClick={() => handleSave.mutate()}>{localization.misc.save}</Button>
</Stack>
</Stack>
Expand All @@ -120,4 +140,4 @@ const buttonContainerStyle = {
justifyContent: "center"
}

export default CreationContent
export default CreationContent
10 changes: 8 additions & 2 deletions frontend/src/queries/project-requests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ export function updateProject(projectId: string, projectData: {}): AxiosPromise
return apiClient.put(urlAddress.api.project(projectId), projectData)
}

export function createProject(projectId: string, projectData: {}): AxiosPromise {
export function createProject(projectId: string, projectData: {
projectName: string,
deploymentThreshold: string
}): AxiosPromise {
if (projectData.projectName && projectData.projectName.length > 255) {
return Promise.reject(new Error("Project name exceeds the maximum length of 255 characters"));
}
return apiClient.post(urlAddress.api.createProject(projectId), projectData)
}

Expand All @@ -45,4 +51,4 @@ export function updateProjectCves(projectId: string): AxiosPromise {
export function deleteProjects(projectIds: string[]): AxiosPromise {
let data = {projectIds: projectIds}
return apiClient.post(urlAddress.api.deleteProjects, data)
}
}
2 changes: 1 addition & 1 deletion scripts/run-backend.bash
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ python manage.py createcachetable rate_limit
python manage.py migrate
python manage.py collectstatic --no-input

if [[ ${IS_DEV} == "true" ]] ; then
if [[ ${IS_DEV} == "True" ]] ; then
python manage.py runserver
else
gunicorn securecheckplus.wsgi:application --bind 0.0.0.0:8000 --workers=2 --threads=2 --log-level INFO
Expand Down