Skip to content

Commit

Permalink
Support renaming of component in DMS (#12)
Browse files Browse the repository at this point in the history
* Support renaming of component in DMS

* Change log level

* Remove loggin wrere throw except& fix fryRun param

* Fix test

* Add toComponentDTO mapper

* Delete getComponentReadSecurityGroups

* Return logs, extract arg

* Rename method getComponents
  • Loading branch information
ozonophore authored Mar 5, 2024
1 parent 96c325a commit 6f8b218
Show file tree
Hide file tree
Showing 17 changed files with 238 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ out/
.gradle
build
test_db.mv.db
.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.json.JSONObject
import org.mockserver.client.MockServerClient
import org.mockserver.model.HttpRequest
import org.mockserver.model.HttpResponse
import org.mockserver.model.Parameter.param


abstract class ConfigureMockServer : DefaultTask() {
Expand Down Expand Up @@ -46,18 +45,53 @@ abstract class ConfigureMockServer : DefaultTask() {
).withStatusCode(200)
}
mockServerClient.`when`(
HttpRequest.request().withMethod("GET").withPath("/rest/release-engineering/3/component/ee-component/version/{version}/status")
HttpRequest.request().withMethod("GET").withPath("/rest/release-engineering/3/components/some-ee-component")
.withQueryStringParameter("build_whitelist", "status,version,release_version")
).respond {
val versions = it.getFirstQueryStringParameter("versions").split(',')
val versionsField = it.getFirstQueryStringParameter("versions_field")
val versionStatuses = it.getFirstQueryStringParameter("version_statuses").split(',')
val builds = eeComponentBuilds.filter { build ->
build as JSONObject
versionStatuses.contains(build.getString("status")) && when (versionsField) {
"VERSION" -> versions.contains(build.getString("version"))
"RELEASE_VERSION" -> versions.contains(build.getString("release_version"))
else -> false
}
}
HttpResponse.response().withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.mimeType)
.withBody(
JSONObject().put("name", "some-ee-component")
.put("builds", builds)
.toString(2)
).withStatusCode(200)
}
mockServerClient.`when`(
HttpRequest.request().withMethod("GET").withPath("/rest/release-engineering/3/component/{component-name}")
.withPathParameter("component-name")
).respond {
val component = it.getFirstPathParameter("component-name")
if ("ee-component".equals(component, ignoreCase = true)) {
HttpResponse.response().withStatusCode(200)
} else {
HttpResponse.response().withStatusCode(404)
}
}
mockServerClient.`when`(
HttpRequest.request().withMethod("GET").withPath("/rest/release-engineering/3/component/{component-name}/version/{version}/status")
.withPathParameter("version")
.withPathParameter("component-name")
).respond {
val version = it.getFirstPathParameter("version")
val component = it.getFirstPathParameter("component-name")
val build = eeComponentBuilds.firstOrNull { build ->
build as JSONObject
version == build.getString("version")
} as JSONObject?
if (build != null) {
HttpResponse.response().withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.mimeType)
.withBody(
JSONObject().put("component", "ee-component")
JSONObject().put("component", component)
.put("version", build.getString("version"))
.put("buildVersion", build.getString("version"))
.put("releaseVersion", build.getString("release_version"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import feign.Headers
import feign.Param
import feign.RequestLine
import feign.Response
import org.octopusden.octopus.dms.client.common.dto.ComponentDTO

interface DmsServiceFeignClient {
@RequestLine("GET rest/api/3/components")
Expand Down Expand Up @@ -81,6 +82,13 @@ interface DmsServiceFeignClient {
@Param("artifact-id") artifactId: Long
)

@RequestLine("POST /rest/api/3/admin/rename-component/{component-name}/{new-component-name}?dry-run=false")
@Headers("Accept: application/json")
fun renameComponent(
@Param("component-name") componentName: String,
@Param("new-component-name") newComponentName: String
)

@RequestLine("GET rest/api/3/configuration")
fun getConfiguration(): PropertiesDTO

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class ClassicDmsServiceClient(
componentName: String, version: String, artifactId: Long
) = client.deleteComponentVersionArtifact(componentName, version, artifactId)

override fun renameComponent(componentName: String, newComponentName: String) =
client.renameComponent(componentName, newComponentName)

override fun getConfiguration() = client.getConfiguration()

override fun getRepositories(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ abstract class DMSException(message: String, val code: String) : RuntimeExceptio
"DMS-40008" to { message: String -> DownloadResultFailureException(message) },
"DMS-40010" to { message: String -> VersionFormatIsNotValidException(message) },
"DMS-40011" to { message: String -> NotFoundException(message) },
"DMS-40012" to { message: String -> IllegalVersionStatusException(message) }
"DMS-40012" to { message: String -> IllegalVersionStatusException(message) },
"DMS-40013" to { message: String -> IllegalComponentRenamingException(message) }
)
}
}
Expand All @@ -26,4 +27,5 @@ class PackagingIsNotSpecifiedException(message: String) : DMSException(message,
class DownloadResultFailureException(message: String) : DMSException(message, "DMS-40008")
class VersionFormatIsNotValidException(message: String) : DMSException(message, "DMS-40010")
class NotFoundException(message: String) : DMSException(message, "DMS-40011")
class IllegalVersionStatusException(message: String) : DMSException(message, "DMS-40012")
class IllegalVersionStatusException(message: String) : DMSException(message, "DMS-40012")
class IllegalComponentRenamingException(message: String) : DMSException(message, "DMS-40013")
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package org.octopusden.octopus.dms.controller
import org.octopusden.octopus.dms.service.AdminService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.MediaType
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
Expand Down Expand Up @@ -40,4 +42,13 @@ class AdminController(
fun deleteOrphanedArtifacts(
@RequestParam("dry-run", defaultValue = "true", required = false) dryRun: Boolean
) = adminService.deleteOrphanedArtifacts(dryRun)

@Operation(summary = "Rename a component")
@PostMapping("rename-component/{component-name}/{new-component-name}", produces = [MediaType.APPLICATION_JSON_VALUE])
fun renameComponent(
@PathVariable(name = "component-name") componentName: String,
@PathVariable(name = "new-component-name") newComponentName: String,
@RequestParam("dry-run", defaultValue = "true", required = false) dryRun: Boolean,
) = adminService.renameComponent(componentName, newComponentName, dryRun)

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.octopusden.octopus.dms.exception.UnknownArtifactTypeException
import org.octopusden.octopus.dms.exception.VersionFormatIsNotValidException
import org.octopusden.octopus.dms.client.common.dto.ApplicationErrorResponse
import feign.FeignException
import org.octopusden.octopus.dms.exception.IllegalComponentRenamingException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.slf4j.Logger
Expand Down Expand Up @@ -41,7 +42,8 @@ class ExceptionHandler(private val objectMapper: ObjectMapper) {
PackagingIsNotSpecifiedException::class,
DownloadResultFailureException::class,
VersionFormatIsNotValidException::class,
IllegalVersionStatusException::class
IllegalVersionStatusException::class,
IllegalComponentRenamingException::class
)
@Order(5)
fun handle(request: HttpServletRequest, response: HttpServletResponse, e: DMSException) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class PermissionEvaluator(
securityService.getCurrentUser(),
componentName,
try {
componentsRegistryService.getComponentReadSecurityGroups(componentName)
componentsRegistryService.getComponent(componentName).securityGroups.read
} catch (e: Exception) {
log.warn("Unable to get read security groups for component '$componentName'", e)
emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ interface AdminService {
fun deleteInvalidComponentsVersions(dryRun: Boolean)
fun recalculateMinorVersions(dryRun: Boolean)
fun deleteOrphanedArtifacts(dryRun: Boolean)
fun renameComponent(name: String, newName: String, dryRun: Boolean)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import org.octopusden.octopus.components.registry.core.dto.DetailedComponentVers
import org.octopusden.releng.versions.VersionNames

interface ComponentsRegistryService {
fun getComponents(): List<ComponentDTO>
fun getComponentReadSecurityGroups(component: String): List<String>
/**
* Get component by name
* @param name component name
* @return component
* @throws NotFoundException if component not found
*/
fun getComponent(name: String): ComponentDTO
fun getExplicitExternalComponents(): List<ComponentDTO>
fun checkComponent(component: String)
fun getDetailedComponentVersion(component: String, version: String): DetailedComponentVersion
fun getVersionNames(): VersionNames
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.octopusden.octopus.dms.client.common.dto.ArtifactType
import org.octopusden.octopus.dms.client.common.dto.BuildStatus

interface RelengService {
fun componentExists(component: String): Boolean
fun checkVersionStatus(component: String, version: String, type: ArtifactType? = null)
fun getComponentBuilds(
component: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.octopusden.octopus.dms.service.impl

import org.octopusden.octopus.dms.entity.Component
import org.octopusden.octopus.dms.exception.IllegalComponentRenamingException
import org.octopusden.octopus.dms.exception.NotFoundException
import org.octopusden.octopus.dms.exception.UnableToFindArtifactException
import org.octopusden.octopus.dms.repository.ArtifactRepository
import org.octopusden.octopus.dms.repository.ComponentRepository
Expand All @@ -8,10 +11,12 @@ import org.octopusden.octopus.dms.service.AdminService
import org.octopusden.octopus.dms.service.ArtifactService
import org.octopusden.octopus.dms.service.ComponentService
import org.octopusden.octopus.dms.service.ComponentsRegistryService
import org.octopusden.octopus.dms.service.RelengService
import org.octopusden.octopus.dms.service.StorageService
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.octopusden.octopus.components.registry.core.exceptions.NotFoundException as ComponentsRegistryNotFoundException

@Service
@Transactional(readOnly = false)
Expand All @@ -22,7 +27,8 @@ class AdminServiceImpl( //TODO: move functionality to ComponentService and Artif
private val storageService: StorageService,
private val componentRepository: ComponentRepository,
private val componentVersionRepository: ComponentVersionRepository,
private val artifactRepository: ArtifactRepository
private val artifactRepository: ArtifactRepository,
private val relengService: RelengService
) : AdminService {

override fun deleteInvalidComponents(dryRun: Boolean) {
Expand Down Expand Up @@ -68,6 +74,70 @@ class AdminServiceImpl( //TODO: move functionality to ComponentService and Artif
}
}

/**
* Update component name and return new component name.
* By this point, the component should have already been renamed in 'releng'.
* @param name - old component name
* @param newName - new component name
* @param dryRun - if true, do not update component name
* @throws NotFoundException if component with name [name] not found in releng
*/
@Transactional(readOnly = false)
override fun renameComponent(name: String, newName: String, dryRun: Boolean) {
log.info("Update component name from '$name' to '$newName'")

if (isComponentPresentInRegistry(name)) {
log.error("Component with name $name exists in components registry")
throw IllegalComponentRenamingException("Component with name $name exists in components registry")
}
if (!isComponentPresentInRegistry(newName)) {
log.error("Component with name $newName not found in components registry")
throw NotFoundException("Component with name $newName not found in components registry")
}
if (!relengService.componentExists(newName)) {
throw NotFoundException("Component with name $newName not found in releng")
}
val component = componentRepository.findByName(newName)
val existedComponent = componentRepository.findByName(name)

if (existedComponent != null && component != null) {
with("Both component with name $name and name $newName exists in DMS") {
log.error(this)
throw IllegalComponentRenamingException(this)
}
}

existedComponent?.let {
if (!dryRun) {
componentRepository.save(Component(name = newName, id = it.id))
log.info("Component with name $name updated to $newName")
} else {
log.info("Component with name $name will be updated to $newName")
}
} ?: run {
log.warn("Component with name $name not found in DMS")
if (component == null) {
throw NotFoundException("None of $name and $newName components were found in DMS")
}
log.info("Component $name already renamed to $newName")
}
}

/**
* Check if component with name [newName] exists in components registry
* @param name - the component name
* @return true if component with name [newName] exists in components registry
*/
private fun isComponentPresentInRegistry(name: String): Boolean {
return try{
componentsRegistryService.getComponent(name)
return true
} catch (e: ComponentsRegistryNotFoundException) {
log.info("Component with name $name not found in components registry")
return false
}
}

companion object {
private val log = LoggerFactory.getLogger(AdminServiceImpl::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class ComponentServiceImpl(
) : ComponentService {
override fun getComponents(): List<ComponentDTO> {
log.info("Get components")
return componentsRegistryService.getComponents()
return componentsRegistryService.getExplicitExternalComponents()
}

@Transactional(readOnly = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.octopusden.octopus.dms.exception.NotFoundException
import org.octopusden.octopus.dms.service.ComponentsRegistryService
import org.octopusden.octopus.components.registry.client.impl.ClassicComponentsRegistryServiceClient
import org.octopusden.octopus.components.registry.client.impl.ClassicComponentsRegistryServiceClientUrlProvider
import org.octopusden.octopus.components.registry.core.dto.Component
import org.octopusden.octopus.components.registry.core.dto.DetailedComponentVersion
import org.octopusden.octopus.components.registry.core.dto.VersionRequest
import org.octopusden.releng.versions.NumericVersionFactory
Expand All @@ -27,20 +28,23 @@ class ComponentsRegistryServiceImpl(
}
)

override fun getComponents() = client.getAllComponents().components
override fun getComponent(name: String): ComponentDTO = client.getById(name).let {
it.toComponentDTO()
}

override fun getExplicitExternalComponents() = client.getAllComponents().components
.filter { it.distribution?.let { d -> d.explicit && d.external } ?: false }
.map {
ComponentDTO(
it.id, // Component name
it.name ?: it.id, // Component display name
it.clientCode,
it.parentComponent,
SecurityGroupsDTO(it.distribution?.securityGroups?.read ?: emptyList())
)
it.toComponentDTO()
}

override fun getComponentReadSecurityGroups(component: String) =
client.getById(component).distribution?.securityGroups?.read ?: emptyList()
private fun Component.toComponentDTO() = ComponentDTO(
id,
name ?: id,
clientCode,
parentComponent,
SecurityGroupsDTO(distribution?.securityGroups?.read ?: emptyList())
)

override fun checkComponent(component: String) {
val distribution = client.getById(component).distribution
Expand Down
Loading

0 comments on commit 6f8b218

Please sign in to comment.