Skip to content

Commit

Permalink
Merge branch 'master' into feature/edit-criticality-calculator
Browse files Browse the repository at this point in the history
  • Loading branch information
DrAlexD authored Oct 18, 2023
2 parents a774003 + 1ef7f67 commit 41e034b
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,23 +147,6 @@ class VulnerabilityController(
vulnerabilityService.countByUserNameAndStatus(userName, VulnerabilityStatus.APPROVED)
}

@GetMapping("/by-organization-and-status")
@Operation(
method = "GET",
summary = "Get list of vulnerabilities by organization name.",
description = "Get list of vulnerabilities by organization name.",
)
@ApiResponse(
responseCode = "200",
description = "Successfully fetched list of vulnerabilities by organization name"
)
fun getVulnerabilityByOrganization(
@RequestParam organizationName: String,
@RequestParam status: VulnerabilityStatus,
): Flux<VulnerabilityDto> = blockingToFlux {
vulnerabilityService.findByOrganizationNameAndStatus(organizationName, status)
}.switchIfEmptyToNotFound()

@GetMapping("/user-in-vulnerability")
@Operation(
method = "GET",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,6 @@ class VulnerabilityService(
return vulnerabilityMetadataRepository.countByUserIdAndStatus(user.requiredId(), status)
}

/**
* @param organizationName name of organization
* @param status status of vulnerability
* @return list of vulnerabilities
*/
fun findByOrganizationNameAndStatus(organizationName: String, status: VulnerabilityStatus): List<VulnerabilityDto> =
vulnerabilityMetadataRepository.findByOrganizationNameAndStatus(organizationName, status).map { it.toDto().toVulnerabilityDto() }

/**
* @param identifier id of vulnerability
* @return list of vulnerabilities
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,4 @@ interface VulnerabilityMetadataRepository : BaseEntityRepository<VulnerabilityMe
* @return count of vulnerabilities
*/
fun countByUserIdAndStatus(userId: Long, status: VulnerabilityStatus): Int

/**
* @param organizationName name of organization
* @param status status of vulnerability
* @return list of metadata
*/
fun findByOrganizationNameAndStatus(organizationName: String, status: VulnerabilityStatus): List<VulnerabilityMetadata>
}
Original file line number Diff line number Diff line change
@@ -1,88 +1,96 @@
@file:Suppress(
"FILE_NAME_MATCH_CLASS",
"HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE",
)

package com.saveourtool.save.frontend.components.basic.organizations

import com.saveourtool.save.entities.cosv.VulnerabilityMetadataDto
import com.saveourtool.save.entities.vulnerability.VulnerabilityDto
import com.saveourtool.save.entities.vulnerability.VulnerabilityStatus
import com.saveourtool.save.filters.VulnerabilityFilter
import com.saveourtool.save.frontend.components.tables.TableProps
import com.saveourtool.save.frontend.components.tables.columns
import com.saveourtool.save.frontend.components.tables.enableExpanding
import com.saveourtool.save.frontend.components.tables.tableComponent
import com.saveourtool.save.frontend.components.tables.value
import com.saveourtool.save.frontend.components.views.vuln.component.uploadCosvButton
import com.saveourtool.save.frontend.externals.i18next.useTranslation
import com.saveourtool.save.frontend.utils.*
import com.saveourtool.save.frontend.utils.noopResponseHandler
import com.saveourtool.save.validation.FrontendRoutes
import com.saveourtool.save.validation.FrontendRoutes.VULNERABILITY_SINGLE
import js.core.jso
import react.*
import react.dom.html.ReactHTML.br
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.td
import react.router.dom.Link
import web.cssom.ClassName
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Suppress("MAGIC_NUMBER", "TYPE_ALIAS")
private val vulnerabilityTable: FC<TableProps<VulnerabilityDto>> = tableComponent(
private val vulnerabilitiesUrl = "$apiUrl/vulnerabilities"

private val vulnerabilityTable: FC<VulnerabilityTableProps> = tableComponent(
columns = {
columns {
column(id = "name", header = "Name", { this.identifier }) { cellContext ->
column(id = "name", header = "Name", VulnerabilityDto::identifier) { cellContext ->
Fragment.create {
td {
Link {
to = "/${FrontendRoutes.VULNERABILITY_SINGLE}/${cellContext.row.original.identifier}"
+cellContext.value
val identifier = cellContext.value
to = "/$VULNERABILITY_SINGLE/$identifier"
+identifier
}
}
}
}
column(id = "short_description", header = "Description", { progress }) { cellContext ->
column(id = "short_description", header = "Description", VulnerabilityDto::shortDescription) { cellContext ->
Fragment.create {
td {
+cellContext.row.original.shortDescription
+cellContext.value
}
}
}
column(id = "progress", header = "Criticality", { progress }) { cellContext ->
column(id = "progress", header = "Criticality", VulnerabilityDto::progress) { cellContext ->
Fragment.create {
td {
+"${ cellContext.row.original.progress }"
+cellContext.value.toString()
}
}
}
column(id = "language", header = "Language", { language }) { cellContext ->
column(id = "language", header = "Language", VulnerabilityDto::language) { cellContext ->
Fragment.create {
td {
+"${ cellContext.row.original.language }"
+cellContext.value.toString()
}
}
}
}
},
initialPageSize = 10,
useServerPaging = false,
)
useServerPaging = true,
tableOptionsCustomizer = ChildrenBuilder::enableExpanding
) { props ->
arrayOf(props.count)
}

@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR")
val organizationVulnerabilitiesTab: FC<OrganizationVulnerabilitiesMenuProps> = FC { props ->
val filter = VulnerabilityFilter.approved.copy(
organizationName = props.organizationName,
)
val filterJson = Json.encodeToString(filter)

val (t) = useTranslation("organization")
val (vulnerabilities, setVulnerabilities) = useState<Array<VulnerabilityDto>>(emptyArray())
var count by useState(initialValue = 0)
useRequest {
val fetchedVulnerabilities: Array<VulnerabilityDto> = get(
url = "$apiUrl/vulnerabilities/by-organization-and-status",
params = jso<dynamic> {
organizationName = props.organizationName
status = VulnerabilityStatus.APPROVED
},
count = post(
url = "$vulnerabilitiesUrl/count/by-filter",
headers = jsonHeaders,
loadingHandler = ::loadingHandler,
body = filterJson,
loadingHandler = ::noopLoadingHandler,
responseHandler = ::noopResponseHandler,
).unsafeMap {
it.decodeFromJsonString()
).unsafeMap { response ->
response.decodeFromJsonString()
}
setVulnerabilities(fetchedVulnerabilities)
}
div {
className = ClassName("col-8 mx-auto mt-1 mb-3")
Expand All @@ -94,11 +102,30 @@ val organizationVulnerabilitiesTab: FC<OrganizationVulnerabilitiesMenuProps> = F
isImage = false
}
}
if (vulnerabilities.isNotEmpty()) {
if (count != 0) {
vulnerabilityTable {
getData = { _, _ ->
vulnerabilities
getData = { pageIndex, pageSize ->
post(
url = "$vulnerabilitiesUrl/by-filter",
params = jso<dynamic> {
page = pageIndex
size = pageSize
},
headers = jsonHeaders,
body = filterJson,
loadingHandler = ::loadingHandler,
responseHandler = ::noopResponseHandler,
)
.unsafeMap<Array<out VulnerabilityMetadataDto>> { response ->
response.decodeFromJsonString()
}
.map(VulnerabilityMetadataDto::toVulnerabilityDto)
.toTypedArray()
}
getPageCount = { pageSize ->
pageCount(count, pageSize)
}
this.count = count
}
} else {
renderTablePlaceholder("text-center p-4 bg-white", "dashed") {
Expand All @@ -115,6 +142,18 @@ val organizationVulnerabilitiesTab: FC<OrganizationVulnerabilitiesMenuProps> = F
}
}

/**
* The properties of [vulnerabilityTable].
*
* @see vulnerabilityTable
*/
external interface VulnerabilityTableProps : TableProps<VulnerabilityDto> {
/**
* The total count of approved vulnerabilities for this organization.
*/
var count: Int
}

/**
* OrganizationVulnerabilitiesMenu component props
*/
Expand All @@ -129,3 +168,25 @@ external interface OrganizationVulnerabilitiesMenuProps : Props {
*/
var isMember: Boolean
}

/**
* @return the page count, using the [total] number of items and the [pageSize]
* items per page.
*/
private fun pageCount(total: Int, pageSize: Int): Int {
require(total >= 0) {
"total should be non-negative: $total"
}
require(pageSize > 0) {
"pageSize should be positive: $pageSize"
}

/*-
* Source: "Number Conversion" by Roland Backhouse
* (http://www.cs.nott.ac.uk/~psarb2/G51MPC/slides/NumberLogic.pdf).
*
* This shouldn't be simplified: otherwise, if `total` is 0,
* the page count of 1 would still be returned.
*/
return (total + pageSize - 1) / pageSize
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ val projectSettingsMenu: FC<ProjectSettingsMenuProps> = FC { props ->
message = "Are you sure you want to delete the project $projectPath?"
clickMessage = "Also ban this project"
onActionSuccess = { _ ->
navigate(to = "/organization/${props.project.organizationName}/${OrganizationMenuBar.TOOLS.name.lowercase()}")
navigate(to = "/${props.project.organizationName}")
}
buttonStyleBuilder = { childrenBuilder ->
with(childrenBuilder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal class OrganizationAdminView : AbstractView<Props, OrganizationAdminStat
className = ClassName(stringClassName)
when (organization.status) {
OrganizationStatus.CREATED -> Link {
to = "/organization/$organizationName/tools"
to = "/$organizationName"
+organizationName
}
else -> +organizationName
Expand Down

0 comments on commit 41e034b

Please sign in to comment.