All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.nemerosa.ontrack.boot.ui.AdminController.kt Maven / Gradle / Ivy

There is a newer version: 4.4.5
Show newest version
package net.nemerosa.ontrack.boot.ui

import net.nemerosa.ontrack.job.JobScheduler
import net.nemerosa.ontrack.job.JobStatus
import net.nemerosa.ontrack.model.Ack
import net.nemerosa.ontrack.model.security.ApplicationManagement
import net.nemerosa.ontrack.model.security.EncryptionService
import net.nemerosa.ontrack.model.security.SecurityService
import net.nemerosa.ontrack.model.structure.NameDescription
import net.nemerosa.ontrack.model.support.ApplicationLogEntry
import net.nemerosa.ontrack.model.support.ApplicationLogEntryFilter
import net.nemerosa.ontrack.model.support.ApplicationLogService
import net.nemerosa.ontrack.model.support.Page
import net.nemerosa.ontrack.ui.controller.AbstractResourceController
import net.nemerosa.ontrack.ui.resource.Pagination
import net.nemerosa.ontrack.ui.resource.Resource
import net.nemerosa.ontrack.ui.resource.Resources
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.endpoint.HealthEndpoint
import org.springframework.boot.actuate.health.Health
import org.springframework.http.HttpEntity
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on
import org.springframework.web.util.UriComponentsBuilder
import java.net.URI
import javax.validation.Valid
import kotlin.reflect.full.memberProperties

@RestController
@RequestMapping("/admin")
class AdminController
@Autowired
constructor(
        private val jobScheduler: JobScheduler,
        private val applicationLogService: ApplicationLogService,
        private val healthEndpoint: HealthEndpoint,
        private val securityService: SecurityService,
        private val encryptionService: EncryptionService
) : AbstractResourceController() {

    /**
     * Gets the health status
     */
    @GetMapping("status")
    fun getStatus(): Resource = Resource.of(
            healthEndpoint.invoke(),
            uri(on(javaClass).getStatus())
    )

    /**
     * Gets the list of application log entries
     */
    @GetMapping("logs")
    fun getLogEntries(filter: ApplicationLogEntryFilter, page: Page): Resources {
        // Gets the entries
        val entries = applicationLogService.getLogEntries(
                filter,
                page
        )
        // Builds the resources
        val resources = Resources.of(
                entries,
                uri(on(javaClass).getLogEntries(filter, page))
        )
        // Pagination information
        val offset = page.offset
        val count = page.count
        val actualCount = entries.size
        val total = applicationLogService.logEntriesTotal
        var pagination = Pagination.of(offset, actualCount, total)
        // Previous page
        if (offset > 0) {
            pagination = pagination.withPrev(
                    uri(on(AdminController::class.java).getLogEntries(
                            filter,
                            Page(
                                    Math.max(0, offset - count),
                                    count
                            )
                    ))
            )
        }
        // Next page
        if (offset + count < total) {
            pagination = pagination.withNext(
                    uri(on(AdminController::class.java).getLogEntries(
                            filter,
                            Page(
                                    offset + count,
                                    count
                            )
                    ))
            )
        }
        // OK
        return resources.withPagination(pagination)
    }

    /**
     * Deletes all application log entries
     */
    @DeleteMapping("logs")
    fun deleteLogEntries(): Ack {
        applicationLogService.deleteLogEntries()
        return Ack.OK
    }

    /**
     * Gets the job filters
     */
    @GetMapping("jobs/filter")
    fun getJobFilter(): JobFilterResources {
        // All job types
        val types = jobScheduler.allJobKeys
                .map { it.type }
                .distinctBy { it.key }
        // All categories
        val categories = types
                .map { it.category }
                .distinctBy { it.key }
                .map {
                    NameDescription(
                            it.key,
                            it.name
                    )
                }
        // Indexation of types per category
        val indexedTypes = types.groupBy { it.category.key }
        // OK
        return JobFilterResources(
                categories,
                indexedTypes.mapValues { (_, typeList) ->
                    typeList.map { NameDescription(it.key, it.name) }
                }
        )
    }

    /**
     * Gets the list of jobs and their status
     */
    @GetMapping("jobs")
    fun getJobs(
            @Valid jobFilter: JobFilter?,
            page: Page?
    ): Resources {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        val jobs = (jobFilter ?: JobFilter()).filter(jobScheduler.jobStatuses)
        val pagination = Pagination.paginate(
                jobs,
                page ?: Page(),
                { offset, limit ->
                    uri(on(javaClass).getJobs(null, null)).map(jobFilter, Page(offset, limit))
                }
        )
        return Resources.of(
                pagination.items,
                uri(on(javaClass).getJobs(jobFilter, page))
        )
                .withPagination(pagination.pagination)
                .with(
                        "_pause",
                        uri(on(javaClass).pauseAllJobs()),
                        !jobScheduler.isPaused
                )
                .with(
                        "_resume",
                        uri(on(javaClass).resumeAllJobs()),
                        jobScheduler.isPaused
                )
    }

    /**
     * Launches a job
     */
    @PostMapping("jobs/{id:\\d+}")
    fun launchJob(@PathVariable id: Long): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        return jobScheduler.getJobKey(id)
                .map { key -> Ack.validate(jobScheduler.fireImmediately(key) != null) }
                .orElse(Ack.NOK)
    }

    /**
     * Pauses all job executions
     */
    @PutMapping("jobs/pause")
    fun pauseAllJobs(): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        jobScheduler.pause()
        return Ack.OK
    }

    /**
     * Resumes all job executions
     */
    @PutMapping("jobs/resume")
    fun resumeAllJobs(): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        jobScheduler.resume()
        return Ack.OK
    }

    /**
     * Pauses a job
     */
    @PostMapping("jobs/{id:\\d+}/pause")
    fun pauseJob(@PathVariable id: Long): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        return jobScheduler.getJobKey(id)
                .map { key -> Ack.validate(jobScheduler.pause(key)) }
                .orElse(Ack.NOK)
    }

    /**
     * Resumes a job
     */
    @PostMapping("jobs/{id:\\d+}/resume")
    fun resumeJob(@PathVariable id: Long): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        return jobScheduler.getJobKey(id)
                .map { key -> Ack.validate(jobScheduler.resume(key)) }
                .orElse(Ack.NOK)
    }

    /**
     * Deleting a job
     */
    @DeleteMapping("jobs/{id:\\d+}")
    fun deleteJob(@PathVariable id: Long): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        return jobScheduler.getJobKey(id)
                .filter { key -> !jobScheduler.getJobStatus(key).get().isValid }
                .map { key -> Ack.validate(jobScheduler.unschedule(key)) }
                .orElse(Ack.NOK)
    }

    /**
     * Stopping a job
     */
    @DeleteMapping("jobs/{id:\\d+}/stop")
    fun stopJob(@PathVariable id: Long): Ack {
        securityService.checkGlobalFunction(ApplicationManagement::class.java)
        return jobScheduler.getJobKey(id)
                .map { key -> Ack.validate(jobScheduler.stop(key)) }
                .orElse(Ack.NOK)
    }

    /**
     * Exporting the encryption key
     */
    @GetMapping("/encryption")
    fun exportEncryptionKey(): HttpEntity {
        return ResponseEntity.ok(encryptionService.exportKey())
    }

    /**
     * Importing the encryption key
     */
    @PutMapping("/encryption")
    fun importEncryptionKey(@RequestBody payload: String): HttpEntity {
        encryptionService.importKey(payload)
        return ResponseEntity.accepted().body(payload)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy