From 40f0fffe27428b8004d7646d37cf18ce0d5aeeda Mon Sep 17 00:00:00 2001 From: Damien Coraboeuf Date: Sun, 15 Dec 2024 15:24:40 +0100 Subject: [PATCH] #1236 Environment display options for a build --- .../promotions/EnvironmentBuildCount.kt | 12 + ...EnvironmentsBuildPromotionInfoExtension.kt | 97 ++--- .../environments/service/SlotService.kt | 5 + .../environments/service/SlotServiceImpl.kt | 11 + .../settings/EnvironmentsSettings.kt | 8 + .../EnvironmentsSettingsBuildDisplayOption.kt | 20 + .../settings/EnvironmentsSettingsCasc.kt | 17 + .../settings/EnvironmentsSettingsManager.kt | 32 ++ .../settings/EnvironmentsSettingsProvider.kt | 23 ++ .../ui/GQLTypeEnvironmentBuildCount.kt | 34 ++ .../extension/environments/SlotTestSupport.kt | 7 +- ...vironmentsBuildPromotionInfoExtensionIT.kt | 288 +++++++------ ...sBuildPromotionInfoExtensionTestSupport.kt | 143 +++++++ .../settings/EnvironmentsSettingsCascIT.kt | 58 +++ .../settings/EnvironmentsSettingsIT.kt | 32 ++ ...ntsBuildPromotionInfoExtensionGraphQLIT.kt | 387 ++++++++++-------- .../components/builds/BuildPromotionInfo.js | 22 +- .../environments/EnvironmentGraphQL.js | 5 + .../EnvironmentBuildCount/Actions.js | 14 + .../{Slot => EnvironmentBuildCount}/Dot.js | 2 +- .../EnvironmentBuildCount/Name.js | 15 + .../build-promotion-info-item/Slot/Actions.js | 75 ---- .../build-promotion-info-item/Slot/Name.js | 12 - .../SlotPipeline/Actions.js | 13 +- .../SlotPipeline/Dot.js | 6 +- .../SlotPipeline/Name.js | 7 +- .../framework/settings/environments-form.js | 25 ++ ontrack-web-core/ontrack.graphql | 10 +- 28 files changed, 918 insertions(+), 462 deletions(-) create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentBuildCount.kt create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettings.kt create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsBuildDisplayOption.kt create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCasc.kt create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsManager.kt create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsProvider.kt create mode 100644 ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/ui/GQLTypeEnvironmentBuildCount.kt create mode 100644 ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionTestSupport.kt create mode 100644 ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCascIT.kt create mode 100644 ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsIT.kt create mode 100644 ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Actions.js rename ontrack-web-core/components/framework/build-promotion-info-item/{Slot => EnvironmentBuildCount}/Dot.js (57%) create mode 100644 ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Name.js delete mode 100644 ontrack-web-core/components/framework/build-promotion-info-item/Slot/Actions.js delete mode 100644 ontrack-web-core/components/framework/build-promotion-info-item/Slot/Name.js create mode 100644 ontrack-web-core/components/framework/settings/environments-form.js diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentBuildCount.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentBuildCount.kt new file mode 100644 index 0000000000..da6fffeeb9 --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentBuildCount.kt @@ -0,0 +1,12 @@ +package net.nemerosa.ontrack.extension.environments.promotions + +import net.nemerosa.ontrack.model.annotations.APIDescription +import net.nemerosa.ontrack.model.structure.Build + +@APIDescription("Number of environments where a build is deployed") +data class EnvironmentBuildCount( + @APIDescription("Associated build") + val build: Build, + @APIDescription("Number of environments where a build is deployed") + val count: Int, +) diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtension.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtension.kt index c3dc936671..f47c9de90d 100644 --- a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtension.kt +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtension.kt @@ -1,12 +1,14 @@ package net.nemerosa.ontrack.extension.environments.promotions import net.nemerosa.ontrack.extension.api.BuildPromotionInfoExtension -import net.nemerosa.ontrack.extension.environments.* -import net.nemerosa.ontrack.extension.environments.rules.PromotionRelatedSlotAdmissionRule -import net.nemerosa.ontrack.extension.environments.rules.SlotAdmissionRuleRegistry +import net.nemerosa.ontrack.extension.environments.EnvironmentsExtensionFeature +import net.nemerosa.ontrack.extension.environments.SlotPipeline import net.nemerosa.ontrack.extension.environments.service.SlotService +import net.nemerosa.ontrack.extension.environments.settings.EnvironmentsSettings +import net.nemerosa.ontrack.extension.environments.settings.EnvironmentsSettingsBuildDisplayOption import net.nemerosa.ontrack.extension.support.AbstractExtension import net.nemerosa.ontrack.model.promotions.BuildPromotionInfoItem +import net.nemerosa.ontrack.model.settings.CachedSettingsService import net.nemerosa.ontrack.model.structure.Build import net.nemerosa.ontrack.model.structure.PromotionLevel import org.springframework.stereotype.Component @@ -15,13 +17,13 @@ import kotlin.reflect.KClass @Component class EnvironmentsBuildPromotionInfoExtension( extensionFeature: EnvironmentsExtensionFeature, + private val cachedSettingsService: CachedSettingsService, private val slotService: SlotService, - private val slotAdmissionRuleRegistry: SlotAdmissionRuleRegistry, ) : AbstractExtension(extensionFeature), BuildPromotionInfoExtension { override val types: Collection> = setOf( - Slot::class, SlotPipeline::class, + EnvironmentBuildCount::class, ) override fun buildPromotionInfoItems( @@ -29,65 +31,54 @@ class EnvironmentsBuildPromotionInfoExtension( build: Build, promotionLevels: List ) { - val contributions = mutableListOf>() - // Pipelines for this build - val buildPipelines = slotService.findPipelineByBuild(build) - // Gets the eligible slots for this build - val eligibleSlots = slotService.getEligibleSlotsForBuild(build) - .filter { it.eligible } - .map { it.slot } - .sortedByDescending { it.environment.order } - eligibleSlots.forEach { slot -> - // Getting the promotion level for this slot - val promotionLevel = promotionLevels.firstOrNull { pl -> - isSlotForPromotionLevel(slot, pl) - } - // Adding the contribution for this slot - contributions += buildPromotionInfoItemForEligibleSlot(slot, promotionLevel) - // Getting the pipelines for this slot & build - val pipelines = buildPipelines.filter { it.slot.id == slot.id } - .sortedByDescending { it.number } - pipelines.forEach { pipeline -> - contributions += buildPromotionInfoItemForSlotPipeline(pipeline, promotionLevel) - } + val (buildDisplayOption) = cachedSettingsService.getCachedSettings(EnvironmentsSettings::class.java) + return when (buildDisplayOption) { + EnvironmentsSettingsBuildDisplayOption.ALL -> allEnvironments(items, build) + EnvironmentsSettingsBuildDisplayOption.HIGHEST -> highestEnvironment(items, build) + EnvironmentsSettingsBuildDisplayOption.COUNT -> countEnvironments(items, build) } - // Add the contributions before all the other items - items.addAll(0, contributions) } - private fun isSlotForPromotionLevel( - slot: Slot, - promotionLevel: PromotionLevel - ): Boolean { - return slotService.getAdmissionRuleConfigs(slot) - .count { config -> - val rule = slotAdmissionRuleRegistry.getRule(config.ruleId) - isAdmissionRuleConfigForPromotionLevel(rule, config, promotionLevel) - } == 1 + private fun countEnvironments( + items: MutableList>, + build: Build + ) { + val count = slotService.findSlotPipelinesWhereBuildIsLastDeployed(build).size + items.add(0, buildPromotionInfoItemForDeployedSlotCount(build, count)) } - private fun isAdmissionRuleConfigForPromotionLevel( - rule: SlotAdmissionRule, - config: SlotAdmissionRuleConfig, - promotionLevel: PromotionLevel - ): Boolean { - if (rule is PromotionRelatedSlotAdmissionRule) { - val ruleConfig = rule.parseConfig(config.ruleConfig) - return rule.isForPromotionLevel(ruleConfig, promotionLevel) - } else { - return false + private fun highestEnvironment( + items: MutableList>, + build: Build + ) { + val slotPipeline = slotService.findSlotPipelinesWhereBuildIsLastDeployed(build).firstOrNull() + if (slotPipeline != null) { + items.add(0, buildPromotionInfoItemForDeployedSlotPipeline(slotPipeline)) } } - private fun buildPromotionInfoItemForEligibleSlot(slot: Slot, promotionLevel: PromotionLevel?) = + private fun allEnvironments( + items: MutableList>, + build: Build + ) { + val slotPipelines = slotService.findSlotPipelinesWhereBuildIsLastDeployed(build) + items.addAll(0, slotPipelines.map { slotPipeline -> + buildPromotionInfoItemForDeployedSlotPipeline(slotPipeline) + }) + } + + private fun buildPromotionInfoItemForDeployedSlotPipeline( + slotPipeline: SlotPipeline, + ) = BuildPromotionInfoItem( - promotionLevel = promotionLevel, - data = slot, + promotionLevel = null, + data = slotPipeline, ) - private fun buildPromotionInfoItemForSlotPipeline(slotPipeline: SlotPipeline, promotionLevel: PromotionLevel?) = + private fun buildPromotionInfoItemForDeployedSlotCount(build: Build, count: Int) = BuildPromotionInfoItem( - promotionLevel = promotionLevel, - data = slotPipeline, + promotionLevel = null, + data = EnvironmentBuildCount(build, count), ) + } \ No newline at end of file diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotService.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotService.kt index c054f78fa1..5da2b549d4 100644 --- a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotService.kt +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotService.kt @@ -200,4 +200,9 @@ interface SlotService { */ fun findEligibleSlotsByBuild(build: Build): List + /** + * Gets all the slot pipelines where the given [build] is the last being deployed. + */ + fun findSlotPipelinesWhereBuildIsLastDeployed(build: Build): List + } \ No newline at end of file diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotServiceImpl.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotServiceImpl.kt index 310b01a2ac..d30b40dc96 100644 --- a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotServiceImpl.kt +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/service/SlotServiceImpl.kt @@ -95,6 +95,17 @@ class SlotServiceImpl( }.sortedBy { it.environment.order } } + override fun findSlotPipelinesWhereBuildIsLastDeployed(build: Build): List { + val projectSlots = findSlotsByProject(build.project) + return projectSlots.mapNotNull { slot -> + getCurrentPipeline(slot) + }.filter { pipeline -> + pipeline.build.id == build.id + }.sortedByDescending { pipeline -> + pipeline.slot.environment.order + } + } + private fun getRequiredInput( pipeline: SlotPipeline, config: SlotAdmissionRuleConfig, diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettings.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettings.kt new file mode 100644 index 0000000000..ba4df5fb72 --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettings.kt @@ -0,0 +1,8 @@ +package net.nemerosa.ontrack.extension.environments.settings + +import net.nemerosa.ontrack.model.annotations.APIDescription + +data class EnvironmentsSettings( + @APIDescription("How the environments a build is deployed into are displayed") + val buildDisplayOption: EnvironmentsSettingsBuildDisplayOption = EnvironmentsSettingsBuildDisplayOption.HIGHEST, +) diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsBuildDisplayOption.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsBuildDisplayOption.kt new file mode 100644 index 0000000000..3032d6cf94 --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsBuildDisplayOption.kt @@ -0,0 +1,20 @@ +package net.nemerosa.ontrack.extension.environments.settings + +enum class EnvironmentsSettingsBuildDisplayOption { + + /** + * All environments must be displayed + */ + ALL, + + /** + * Only the highest environment is displayed + */ + HIGHEST, + + /** + * Only a count of the environments is displayed + */ + COUNT, + +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCasc.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCasc.kt new file mode 100644 index 0000000000..a52f67d7db --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCasc.kt @@ -0,0 +1,17 @@ +package net.nemerosa.ontrack.extension.environments.settings + +import net.nemerosa.ontrack.extension.casc.context.settings.AbstractSubSettingsContext +import net.nemerosa.ontrack.model.settings.CachedSettingsService +import net.nemerosa.ontrack.model.settings.SettingsManagerService +import org.springframework.stereotype.Component + +@Component +class EnvironmentsSettingsCasc( + settingsManagerService: SettingsManagerService, + cachedSettingsService: CachedSettingsService +) : AbstractSubSettingsContext( + "environments", + EnvironmentsSettings::class, + settingsManagerService, + cachedSettingsService +) diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsManager.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsManager.kt new file mode 100644 index 0000000000..4707817f10 --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsManager.kt @@ -0,0 +1,32 @@ +package net.nemerosa.ontrack.extension.environments.settings + +import net.nemerosa.ontrack.model.form.Form +import net.nemerosa.ontrack.model.security.SecurityService +import net.nemerosa.ontrack.model.settings.AbstractSettingsManager +import net.nemerosa.ontrack.model.settings.CachedSettingsService +import net.nemerosa.ontrack.model.support.SettingsRepository +import net.nemerosa.ontrack.model.support.setEnum +import org.springframework.stereotype.Component + +@Component +class EnvironmentsSettingsManager( + cachedSettingsService: CachedSettingsService, + securityService: SecurityService, + private val settingsRepository: SettingsRepository, +) : AbstractSettingsManager( + EnvironmentsSettings::class.java, + cachedSettingsService, + securityService +) { + + override fun doSaveSettings(settings: EnvironmentsSettings) { + settingsRepository.setEnum(settings::buildDisplayOption) + } + + override fun getId(): String = "environments" + + override fun getTitle(): String = "Environments" + + @Deprecated("Deprecated in Java") + override fun getSettingsForm(settings: EnvironmentsSettings?): Form = Form.create() +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsProvider.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsProvider.kt new file mode 100644 index 0000000000..4fed81ca61 --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsProvider.kt @@ -0,0 +1,23 @@ +package net.nemerosa.ontrack.extension.environments.settings + +import net.nemerosa.ontrack.model.settings.SettingsProvider +import net.nemerosa.ontrack.model.support.SettingsRepository +import net.nemerosa.ontrack.model.support.getEnum +import org.springframework.stereotype.Component + +@Component +class EnvironmentsSettingsProvider( + private val settingsRepository: SettingsRepository, +) : SettingsProvider { + + override fun getSettings() = EnvironmentsSettings( + buildDisplayOption = settingsRepository.getEnum( + property = EnvironmentsSettings::buildDisplayOption, + defaultValue = EnvironmentsSettingsBuildDisplayOption.HIGHEST, + ), + ) + + override fun getSettingsClass(): Class = + EnvironmentsSettings::class.java + +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/ui/GQLTypeEnvironmentBuildCount.kt b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/ui/GQLTypeEnvironmentBuildCount.kt new file mode 100644 index 0000000000..b329a0a4c6 --- /dev/null +++ b/ontrack-extension-environments/src/main/java/net/nemerosa/ontrack/extension/environments/ui/GQLTypeEnvironmentBuildCount.kt @@ -0,0 +1,34 @@ +package net.nemerosa.ontrack.extension.environments.ui + +import graphql.Scalars.GraphQLID +import graphql.schema.GraphQLObjectType +import net.nemerosa.ontrack.extension.environments.promotions.EnvironmentBuildCount +import net.nemerosa.ontrack.graphql.schema.GQLType +import net.nemerosa.ontrack.graphql.schema.GQLTypeCache +import net.nemerosa.ontrack.graphql.support.getTypeDescription +import net.nemerosa.ontrack.graphql.support.intField +import net.nemerosa.ontrack.graphql.support.toNotNull +import org.springframework.stereotype.Component + +@Component +class GQLTypeEnvironmentBuildCount : GQLType { + + override fun getTypeName(): String = EnvironmentBuildCount::class.java.simpleName + + override fun createType(cache: GQLTypeCache): GraphQLObjectType = + GraphQLObjectType.newObject() + .name(typeName) + .description(getTypeDescription(EnvironmentBuildCount::class)) + .field { + it.name("id") + .description("ID of the count (build ID)") + .type(GraphQLID.toNotNull()) + .dataFetcher { env -> + val count = env.getSource() + count.build.id() + } + } + .intField(EnvironmentBuildCount::count) + .build() + +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/SlotTestSupport.kt b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/SlotTestSupport.kt index 941e3c5f4b..42493a8da8 100644 --- a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/SlotTestSupport.kt +++ b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/SlotTestSupport.kt @@ -10,6 +10,7 @@ import net.nemerosa.ontrack.model.structure.Project import net.nemerosa.ontrack.test.TestUtils.uid import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component +import kotlin.test.assertTrue @Component class SlotTestSupport : AbstractDSLTestSupport() { @@ -85,8 +86,10 @@ class SlotTestSupport : AbstractDSLTestSupport() { } fun startAndDeployPipeline(pipeline: SlotPipeline) { - slotService.startDeployment(pipeline, dryRun = false) - slotService.finishDeployment(pipeline) + val status = slotService.startDeployment(pipeline, dryRun = false) + assertTrue(status.status, "Pipeline deploying") + val result = slotService.finishDeployment(pipeline) + assertTrue(result.deployed, "Pipeline deployed") } fun withDeployedSlotPipeline( diff --git a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionIT.kt b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionIT.kt index 7a4f831a4e..40dac3bb74 100644 --- a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionIT.kt +++ b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionIT.kt @@ -1,11 +1,8 @@ package net.nemerosa.ontrack.extension.environments.promotions -import net.nemerosa.ontrack.extension.environments.SlotAdmissionRuleTestFixtures import net.nemerosa.ontrack.extension.environments.SlotPipeline -import net.nemerosa.ontrack.extension.environments.SlotTestSupport -import net.nemerosa.ontrack.extension.environments.service.SlotService +import net.nemerosa.ontrack.extension.environments.settings.EnvironmentsSettingsBuildDisplayOption import net.nemerosa.ontrack.it.AbstractDSLTestSupport -import net.nemerosa.ontrack.model.promotions.BuildPromotionInfoService import net.nemerosa.ontrack.model.structure.PromotionRun import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -15,124 +12,179 @@ import kotlin.test.assertNull class EnvironmentsBuildPromotionInfoExtensionIT : AbstractDSLTestSupport() { @Autowired - private lateinit var buildPromotionInfoService: BuildPromotionInfoService + private lateinit var environmentsBuildPromotionInfoExtensionTestSupport: EnvironmentsBuildPromotionInfoExtensionTestSupport - @Autowired - private lateinit var slotService: SlotService + @Test + fun `Getting the promotion info for a build with display option being ALL`() { + environmentsBuildPromotionInfoExtensionTestSupport.withSetup( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.ALL + ) { test -> + + val ( + _, + info, + bronze, + silver, + gold, + runBronze1, + runBronze2, + runSilver, + _, + eligibleSlotWithNoPromotionRulePipeline, + _, + eligibleSlotWithSilverPromotionRulePipeline, + ) = test + + // Checking all items have been collected + assertEquals(6, info.items.size) + var index = 0 + + // First, the slots & NOT their pipelines + + info.items[index++].apply { + assertNull(promotionLevel) + assertEquals(eligibleSlotWithSilverPromotionRulePipeline.id, (data as SlotPipeline).id) + } - @Autowired - private lateinit var slotTestSupport: SlotTestSupport + info.items[index++].apply { + assertNull(promotionLevel) + assertEquals(eligibleSlotWithNoPromotionRulePipeline.id, (data as SlotPipeline).id) + } + + // Then the promotions & their promotion runs + + info.items[index++].apply { + assertEquals(gold, promotionLevel) + assertEquals(gold, data) + } + + info.items[index++].apply { + assertEquals(silver, promotionLevel) + assertEquals(runSilver.id, (data as PromotionRun).id) + } + + info.items[index++].apply { + assertEquals(bronze, promotionLevel) + assertEquals(runBronze2.id, (data as PromotionRun).id) + } + + info.items[index].apply { + assertEquals(bronze, promotionLevel) + assertEquals(runBronze1.id, (data as PromotionRun).id) + } + } + } @Test - fun `Getting the promotion info for a build with some promotions`() { - asAdmin { - project { - branch { - val bronze = promotionLevel("BRONZE") - val silver = promotionLevel("SILVER") - val gold = promotionLevel("GOLD") - - val eligibleSlotWithNoPromotionRule = slotTestSupport.withSlot( - order = 10, - project = project, - ) {} - - val eligibleSlotWithSilverPromotionRule = slotTestSupport.withSlot( - order = 20, - project = project, - ) { - slotService.addAdmissionRuleConfig( - SlotAdmissionRuleTestFixtures.testPromotionAdmissionRuleConfig( - slot = it, - promotion = silver.name, - ) - ) - } - - /* val nonEligibleSlotOnSameProject = */ slotTestSupport.withSlot( - order = 30, - project = project, - ) { - slotService.addAdmissionRuleConfig( - SlotAdmissionRuleTestFixtures.testBranchPatternAdmissionRuleConfig( - slot = it, - includes = listOf("release-.*"), - ) - ) - } - - build { - val runBronze1 = promote(bronze) - val runBronze2 = promote(bronze) - val runSilver = promote(silver) - - val eligibleSlotWithNoPromotionRulePipeline = slotService.startPipeline( - slot = eligibleSlotWithNoPromotionRule, - build = this, - ) - - val eligibleSlotWithSilverPromotionRulePipelines = (1..2).map { - slotService.startPipeline( - slot = eligibleSlotWithSilverPromotionRule, - build = this, - ) - } - - val info = buildPromotionInfoService.getBuildPromotionInfo(this) - - // Checking all items have been collected - assertEquals(9, info.items.size) - - // First, the slots & their pipelines - - info.items[0].apply { - assertEquals(silver, promotionLevel) - assertEquals(eligibleSlotWithSilverPromotionRule, data) - } - - info.items[1].apply { - assertEquals(silver, promotionLevel) - assertEquals(eligibleSlotWithSilverPromotionRulePipelines[1].id, (data as SlotPipeline).id) - } - - info.items[2].apply { - assertEquals(silver, promotionLevel) - assertEquals(eligibleSlotWithSilverPromotionRulePipelines[0].id, (data as SlotPipeline).id) - } - - info.items[3].apply { - assertNull(promotionLevel) - assertEquals(eligibleSlotWithNoPromotionRule, data) - } - - info.items[4].apply { - assertNull(promotionLevel) - assertEquals(eligibleSlotWithNoPromotionRulePipeline.id, (data as SlotPipeline).id) - } - - // Then the promotions & their promotion runs - - info.items[5].apply { - assertEquals(gold, promotionLevel) - assertEquals(gold, data) - } - - info.items[6].apply { - assertEquals(silver, promotionLevel) - assertEquals(runSilver.id, (data as PromotionRun).id) - } - - info.items[7].apply { - assertEquals(bronze, promotionLevel) - assertEquals(runBronze2.id, (data as PromotionRun).id) - } - - info.items[8].apply { - assertEquals(bronze, promotionLevel) - assertEquals(runBronze1.id, (data as PromotionRun).id) - } - } - } + fun `Getting the promotion info for a build with display option being HIGHEST`() { + environmentsBuildPromotionInfoExtensionTestSupport.withSetup( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.HIGHEST + ) { test -> + + val ( + _, + info, + bronze, + silver, + gold, + runBronze1, + runBronze2, + runSilver, + _, + _, + _, + eligibleSlotWithSilverPromotionRulePipeline, + ) = test + + + // Checking all items have been collected + assertEquals(5, info.items.size) + var index = 0 + + // First, the slot where the build is deployed + + info.items[index++].apply { + assertEquals(null, promotionLevel) + assertEquals(eligibleSlotWithSilverPromotionRulePipeline.id, (data as SlotPipeline).id) + } + + // Then the promotions & their promotion runs + + info.items[index++].apply { + assertEquals(gold, promotionLevel) + assertEquals(gold, data) + } + + info.items[index++].apply { + assertEquals(silver, promotionLevel) + assertEquals(runSilver.id, (data as PromotionRun).id) + } + + info.items[index++].apply { + assertEquals(bronze, promotionLevel) + assertEquals(runBronze2.id, (data as PromotionRun).id) + } + + info.items[index].apply { + assertEquals(bronze, promotionLevel) + assertEquals(runBronze1.id, (data as PromotionRun).id) + } + } + } + + @Test + fun `Getting the promotion info for a build with display option being COUNT`() { + environmentsBuildPromotionInfoExtensionTestSupport.withSetup( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.COUNT + ) { test -> + + val ( + build, + info, + bronze, + silver, + gold, + runBronze1, + runBronze2, + runSilver, + _, + _, + _, + _, + ) = test + + + // Checking all items have been collected + assertEquals(5, info.items.size) + var index = 0 + + // First, the count of slots where the build is deployed + + info.items[index++].apply { + assertEquals(null, promotionLevel) + assertEquals(EnvironmentBuildCount(build, count = 2), data) + } + + // Then the promotions & their promotion runs + + info.items[index++].apply { + assertEquals(gold, promotionLevel) + assertEquals(gold, data) + } + + info.items[index++].apply { + assertEquals(silver, promotionLevel) + assertEquals(runSilver.id, (data as PromotionRun).id) + } + + info.items[index++].apply { + assertEquals(bronze, promotionLevel) + assertEquals(runBronze2.id, (data as PromotionRun).id) + } + + info.items[index].apply { + assertEquals(bronze, promotionLevel) + assertEquals(runBronze1.id, (data as PromotionRun).id) } } } diff --git a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionTestSupport.kt b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionTestSupport.kt new file mode 100644 index 0000000000..949929ca76 --- /dev/null +++ b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/promotions/EnvironmentsBuildPromotionInfoExtensionTestSupport.kt @@ -0,0 +1,143 @@ +package net.nemerosa.ontrack.extension.environments.promotions + +import net.nemerosa.ontrack.extension.environments.Slot +import net.nemerosa.ontrack.extension.environments.SlotAdmissionRuleTestFixtures +import net.nemerosa.ontrack.extension.environments.SlotPipeline +import net.nemerosa.ontrack.extension.environments.SlotTestSupport +import net.nemerosa.ontrack.extension.environments.service.SlotService +import net.nemerosa.ontrack.extension.environments.settings.EnvironmentsSettings +import net.nemerosa.ontrack.extension.environments.settings.EnvironmentsSettingsBuildDisplayOption +import net.nemerosa.ontrack.it.AbstractDSLTestSupport +import net.nemerosa.ontrack.model.promotions.BuildPromotionInfo +import net.nemerosa.ontrack.model.promotions.BuildPromotionInfoService +import net.nemerosa.ontrack.model.structure.Build +import net.nemerosa.ontrack.model.structure.PromotionLevel +import net.nemerosa.ontrack.model.structure.PromotionRun +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component +import kotlin.test.assertEquals + +@Component +class EnvironmentsBuildPromotionInfoExtensionTestSupport : AbstractDSLTestSupport() { + + @Autowired + private lateinit var buildPromotionInfoService: BuildPromotionInfoService + + @Autowired + private lateinit var slotService: SlotService + + @Autowired + private lateinit var slotTestSupport: SlotTestSupport + + fun withSetup( + buildDisplayOption: EnvironmentsSettingsBuildDisplayOption, + test: (info: TestInfo) -> Unit, + ) { + asAdmin { + withSettings { + settingsManagerService.saveSettings( + EnvironmentsSettings( + buildDisplayOption = buildDisplayOption, + ) + ) + project { + branch { + val bronze = promotionLevel("BRONZE") + val silver = promotionLevel("SILVER") + val gold = promotionLevel("GOLD") + + val eligibleSlotWithNoPromotionRule = slotTestSupport.withSlot( + order = 10, + project = project, + ) {} + + val eligibleSlotWithSilverPromotionRule = slotTestSupport.withSlot( + order = 20, + project = project, + ) { + slotService.addAdmissionRuleConfig( + SlotAdmissionRuleTestFixtures.testPromotionAdmissionRuleConfig( + slot = it, + promotion = silver.name, + ) + ) + } + + /* val nonEligibleSlotOnSameProject = */ slotTestSupport.withSlot( + order = 30, + project = project, + ) { + slotService.addAdmissionRuleConfig( + SlotAdmissionRuleTestFixtures.testBranchPatternAdmissionRuleConfig( + slot = it, + includes = listOf("release-.*"), + ) + ) + } + + build { + val runBronze1 = promote(bronze) + val runBronze2 = promote(bronze) + val runSilver = promote(silver) + + val eligibleSlotWithNoPromotionRulePipeline = slotService.startPipeline( + slot = eligibleSlotWithNoPromotionRule, + build = this, + ).apply { + slotTestSupport.startAndDeployPipeline(this) + } + + val eligibleSlotWithSilverPromotionRulePipeline = + slotService.startPipeline( + slot = eligibleSlotWithSilverPromotionRule, + build = this, + ).apply { + slotTestSupport.startAndDeployPipeline(this) + } + + // Checking that everything is marked as deployed + val pipelines = slotService.findSlotPipelinesWhereBuildIsLastDeployed(this) + assertEquals(2, pipelines.size, "All pipelines deployed") + + val info = buildPromotionInfoService.getBuildPromotionInfo(this) + + // Testing the info + test( + TestInfo( + build = this, + info = info, + bronze = bronze, + silver = silver, + gold = gold, + runBronze1 = runBronze1, + runBronze2 = runBronze2, + runSilver = runSilver, + eligibleSlotWithNoPromotionRule = eligibleSlotWithNoPromotionRule, + eligibleSlotWithNoPromotionRulePipeline = eligibleSlotWithNoPromotionRulePipeline, + eligibleSlotWithSilverPromotionRule = eligibleSlotWithSilverPromotionRule, + eligibleSlotWithSilverPromotionRulePipeline = eligibleSlotWithSilverPromotionRulePipeline, + ) + ) + } + } + } + } + } + } + + data class TestInfo( + val build: Build, + val info: BuildPromotionInfo, + val bronze: PromotionLevel, + val silver: PromotionLevel, + val gold: PromotionLevel, + val runBronze1: PromotionRun, + val runBronze2: PromotionRun, + val runSilver: PromotionRun, + val eligibleSlotWithNoPromotionRule: Slot, + val eligibleSlotWithNoPromotionRulePipeline: SlotPipeline, + val eligibleSlotWithSilverPromotionRule: Slot, + val eligibleSlotWithSilverPromotionRulePipeline: SlotPipeline, + ) + +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCascIT.kt b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCascIT.kt new file mode 100644 index 0000000000..260d9874bb --- /dev/null +++ b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsCascIT.kt @@ -0,0 +1,58 @@ +package net.nemerosa.ontrack.extension.environments.settings + +import net.nemerosa.ontrack.extension.casc.AbstractCascTestSupport +import net.nemerosa.ontrack.json.asJson +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import kotlin.test.assertEquals + +class EnvironmentsSettingsCascIT : AbstractCascTestSupport() { + + @Autowired + private lateinit var environmentsSettingsCasc: EnvironmentsSettingsCasc + + @Test + fun `Environment settings`() { + asAdmin { + withCleanSettings { + casc( + """ + ontrack: + config: + settings: + environments: + buildDisplayOption: ALL + """.trimIndent() + ) + val settings = cachedSettingsService.getCachedSettings(EnvironmentsSettings::class.java) + assertEquals( + EnvironmentsSettings( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.ALL, + ), + settings + ) + } + } + } + + @Test + fun `Rendering the environment settings`() { + asAdmin { + withCleanSettings { + settingsManagerService.saveSettings( + EnvironmentsSettings( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.COUNT, + ) + ) + val json = environmentsSettingsCasc.render() + assertEquals( + mapOf( + "buildDisplayOption" to "COUNT" + ).asJson(), + json + ) + } + } + } + +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsIT.kt b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsIT.kt new file mode 100644 index 0000000000..3c0d9be554 --- /dev/null +++ b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/settings/EnvironmentsSettingsIT.kt @@ -0,0 +1,32 @@ +package net.nemerosa.ontrack.extension.environments.settings + +import net.nemerosa.ontrack.it.AbstractDSLTestSupport +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class EnvironmentsSettingsIT : AbstractDSLTestSupport() { + + @Test + fun `Environment settings`() { + asAdmin { + withCleanSettings { + // Default settings + assertEquals( + EnvironmentsSettingsBuildDisplayOption.HIGHEST, + cachedSettingsService.getCachedSettings(EnvironmentsSettings::class.java).buildDisplayOption + ) + // Saving the settings + settingsManagerService.saveSettings( + EnvironmentsSettings( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.COUNT + ) + ) + assertEquals( + EnvironmentsSettingsBuildDisplayOption.COUNT, + cachedSettingsService.getCachedSettings(EnvironmentsSettings::class.java).buildDisplayOption + ) + } + } + } + +} \ No newline at end of file diff --git a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/ui/EnvironmentsBuildPromotionInfoExtensionGraphQLIT.kt b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/ui/EnvironmentsBuildPromotionInfoExtensionGraphQLIT.kt index ae5cd756a0..3b2b0fb1e2 100644 --- a/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/ui/EnvironmentsBuildPromotionInfoExtensionGraphQLIT.kt +++ b/ontrack-extension-environments/src/test/java/net/nemerosa/ontrack/extension/environments/ui/EnvironmentsBuildPromotionInfoExtensionGraphQLIT.kt @@ -1,8 +1,7 @@ package net.nemerosa.ontrack.extension.environments.ui -import net.nemerosa.ontrack.extension.environments.SlotAdmissionRuleTestFixtures -import net.nemerosa.ontrack.extension.environments.SlotTestSupport -import net.nemerosa.ontrack.extension.environments.service.SlotService +import net.nemerosa.ontrack.extension.environments.promotions.EnvironmentsBuildPromotionInfoExtensionTestSupport +import net.nemerosa.ontrack.extension.environments.settings.EnvironmentsSettingsBuildDisplayOption import net.nemerosa.ontrack.graphql.AbstractQLKTITSupport import net.nemerosa.ontrack.json.asJson import org.junit.jupiter.api.Test @@ -12,191 +11,237 @@ import kotlin.test.assertEquals class EnvironmentsBuildPromotionInfoExtensionGraphQLIT : AbstractQLKTITSupport() { @Autowired - private lateinit var slotService: SlotService - - @Autowired - private lateinit var slotTestSupport: SlotTestSupport + private lateinit var environmentsBuildPromotionInfoExtensionTestSupport: EnvironmentsBuildPromotionInfoExtensionTestSupport @Test - fun `Getting the promotion info for a build with some promotions`() { - asAdmin { - project { - branch { - val bronze = promotionLevel("BRONZE") - val silver = promotionLevel("SILVER") - val gold = promotionLevel("GOLD") + fun `Getting the promotion info for a build with some promotions using HIGHEST option`() { + environmentsBuildPromotionInfoExtensionTestSupport.withSetup( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.HIGHEST, + ) { test -> - val eligibleSlotWithNoPromotionRule = slotTestSupport.withSlot( - order = 10, - project = project, - ) {} - val eligibleSlotWithSilverPromotionRule = slotTestSupport.withSlot( - order = 20, - project = project, - ) { - slotService.addAdmissionRuleConfig( - SlotAdmissionRuleTestFixtures.testPromotionAdmissionRuleConfig( - slot = it, - promotion = silver.name, - ) - ) + val ( + build, + _, + bronze, + silver, + gold, + runBronze1, + runBronze2, + runSilver, + _, + _, + _, + eligibleSlotWithSilverPromotionRulePipeline, + ) = test + + run( + """ + fragment BuildPromotionInfoItemDataContent on BuildPromotionInfoItemData { + __typename + ... on PromotionLevel { + id + } + ... on PromotionRun { + id + } + ... on SlotPipeline { + id + } + ... on EnvironmentBuildCount { + id + count + } + } + query BuildPromotionInfo { + build(id: ${build.id}) { + promotionInfo { + items { + promotionLevel { + name + } + data { + ...BuildPromotionInfoItemDataContent + } + } + } + } } + """.trimIndent() + ) { data -> - /* val nonEligibleSlotOnSameProject = */ slotTestSupport.withSlot( - order = 30, - project = project, - ) { - slotService.addAdmissionRuleConfig( - SlotAdmissionRuleTestFixtures.testBranchPatternAdmissionRuleConfig( - slot = it, - includes = listOf("release-.*"), + assertEquals( + mapOf( + "build" to mapOf( + "promotionInfo" to mapOf( + "items" to listOf( + mapOf( + "promotionLevel" to null, + "data" to mapOf( + "__typename" to "SlotPipeline", + "id" to eligibleSlotWithSilverPromotionRulePipeline.id + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to gold.name, + ), + "data" to mapOf( + "__typename" to "PromotionLevel", + "id" to gold.id.toString() + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to silver.name, + ), + "data" to mapOf( + "__typename" to "PromotionRun", + "id" to runSilver.id.toString() + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to bronze.name, + ), + "data" to mapOf( + "__typename" to "PromotionRun", + "id" to runBronze2.id.toString() + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to bronze.name, + ), + "data" to mapOf( + "__typename" to "PromotionRun", + "id" to runBronze1.id.toString() + ) + ), + ), + ) ) - ) - } + ).asJson(), + data + ) - build { - val runBronze1 = promote(bronze) - val runBronze2 = promote(bronze) - val runSilver = promote(silver) + } + } + } - val eligibleSlotWithNoPromotionRulePipeline = slotService.startPipeline( - slot = eligibleSlotWithNoPromotionRule, - build = this, - ) + @Test + fun `Getting the promotion info for a build with some promotions using COUNT option`() { + environmentsBuildPromotionInfoExtensionTestSupport.withSetup( + buildDisplayOption = EnvironmentsSettingsBuildDisplayOption.COUNT, + ) { test -> - val eligibleSlotWithSilverPromotionRulePipelines = (1..2).map { - slotService.startPipeline( - slot = eligibleSlotWithSilverPromotionRule, - build = this, - ) - } - run( - """ - fragment BuildPromotionInfoItemDataContent on BuildPromotionInfoItemData { - __typename - ... on PromotionLevel { - id - } - ... on PromotionRun { - id - } - ... on Slot { - id + val ( + build, + _, + bronze, + silver, + gold, + runBronze1, + runBronze2, + runSilver, + _, + _, + _, + _, + ) = test + + run( + """ + fragment BuildPromotionInfoItemDataContent on BuildPromotionInfoItemData { + __typename + ... on PromotionLevel { + id + } + ... on PromotionRun { + id + } + ... on SlotPipeline { + id + } + ... on EnvironmentBuildCount { + id + count + } + } + query BuildPromotionInfo { + build(id: ${build.id}) { + promotionInfo { + items { + promotionLevel { + name } - ... on SlotPipeline { - id + data { + ...BuildPromotionInfoItemDataContent } } - query BuildPromotionInfo { - build(id: $id) { - promotionInfo { - items { - promotionLevel { - name - } - data { - ...BuildPromotionInfoItemDataContent - } - } - } - } - } - """.trimIndent() - ) { data -> + } + } + } + """.trimIndent() + ) { data -> - assertEquals( - mapOf( - "build" to mapOf( - "promotionInfo" to mapOf( - "items" to listOf( - mapOf( - "promotionLevel" to mapOf( - "name" to silver.name, - ), - "data" to mapOf( - "__typename" to "Slot", - "id" to eligibleSlotWithSilverPromotionRule.id - ) - ), - mapOf( - "promotionLevel" to mapOf( - "name" to silver.name, - ), - "data" to mapOf( - "__typename" to "SlotPipeline", - "id" to eligibleSlotWithSilverPromotionRulePipelines[1].id - ) - ), - mapOf( - "promotionLevel" to mapOf( - "name" to silver.name, - ), - "data" to mapOf( - "__typename" to "SlotPipeline", - "id" to eligibleSlotWithSilverPromotionRulePipelines[0].id - ) - ), - mapOf( - "promotionLevel" to null, - "data" to mapOf( - "__typename" to "Slot", - "id" to eligibleSlotWithNoPromotionRule.id - ) - ), - mapOf( - "promotionLevel" to null, - "data" to mapOf( - "__typename" to "SlotPipeline", - "id" to eligibleSlotWithNoPromotionRulePipeline.id - ) - ), - mapOf( - "promotionLevel" to mapOf( - "name" to gold.name, - ), - "data" to mapOf( - "__typename" to "PromotionLevel", - "id" to gold.id.toString() - ) - ), - mapOf( - "promotionLevel" to mapOf( - "name" to silver.name, - ), - "data" to mapOf( - "__typename" to "PromotionRun", - "id" to runSilver.id.toString() - ) - ), - mapOf( - "promotionLevel" to mapOf( - "name" to bronze.name, - ), - "data" to mapOf( - "__typename" to "PromotionRun", - "id" to runBronze2.id.toString() - ) - ), - mapOf( - "promotionLevel" to mapOf( - "name" to bronze.name, - ), - "data" to mapOf( - "__typename" to "PromotionRun", - "id" to runBronze1.id.toString() - ) - ), - ), + assertEquals( + mapOf( + "build" to mapOf( + "promotionInfo" to mapOf( + "items" to listOf( + mapOf( + "promotionLevel" to null, + "data" to mapOf( + "__typename" to "EnvironmentBuildCount", + "id" to "${build.id}", + "count" to 2, + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to gold.name, + ), + "data" to mapOf( + "__typename" to "PromotionLevel", + "id" to gold.id.toString() + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to silver.name, + ), + "data" to mapOf( + "__typename" to "PromotionRun", + "id" to runSilver.id.toString() + ) + ), + mapOf( + "promotionLevel" to mapOf( + "name" to bronze.name, + ), + "data" to mapOf( + "__typename" to "PromotionRun", + "id" to runBronze2.id.toString() ) - ) - ).asJson(), - data + ), + mapOf( + "promotionLevel" to mapOf( + "name" to bronze.name, + ), + "data" to mapOf( + "__typename" to "PromotionRun", + "id" to runBronze1.id.toString() + ) + ), + ), ) - } - } - } + ) + ).asJson(), + data + ) + } } } diff --git a/ontrack-web-core/components/builds/BuildPromotionInfo.js b/ontrack-web-core/components/builds/BuildPromotionInfo.js index dfd92c5616..063393b48f 100644 --- a/ontrack-web-core/components/builds/BuildPromotionInfo.js +++ b/ontrack-web-core/components/builds/BuildPromotionInfo.js @@ -5,7 +5,7 @@ import {gql} from "graphql-request"; import {Timeline} from "antd"; import buildPromotionInfoItem from "@components/builds/BuildPromotionInfoItem"; import {useReloadState} from "@components/common/StateUtils"; -import {gqlSlotData, gqlSlotPipelineData} from "@components/extension/environments/EnvironmentGraphQL"; +import {gqlSlotPipelineData} from "@components/extension/environments/EnvironmentGraphQL"; export default function BuildPromotionInfo({build}) { @@ -20,27 +20,15 @@ export default function BuildPromotionInfo({build}) { setLoading(true) client.request( gql` - ${gqlSlotData} ${gqlSlotPipelineData} fragment BuildPromotionInfoItemDataContent on BuildPromotionInfoItemData { __typename - ... on Slot { - id - environment { - id - name - } - project { - id - name - } - qualifier - } ... on SlotPipeline { ...SlotPipelineData - slot { - ...SlotData - } + } + ... on EnvironmentBuildCount { + id + count } ... on PromotionLevel { id diff --git a/ontrack-web-core/components/extension/environments/EnvironmentGraphQL.js b/ontrack-web-core/components/extension/environments/EnvironmentGraphQL.js index 59d4f445e5..92db8e71f7 100644 --- a/ontrack-web-core/components/extension/environments/EnvironmentGraphQL.js +++ b/ontrack-web-core/components/extension/environments/EnvironmentGraphQL.js @@ -22,6 +22,7 @@ export const gqlSlotData = gql` environment { id name + order } } @@ -76,6 +77,10 @@ export const gqlSlotPipelineData = gql` build { ...SlotPipelineBuildData } + slot { + ...SlotData + } } ${gqlSlotPipelineBuildData} + ${gqlSlotData} `; diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Actions.js b/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Actions.js new file mode 100644 index 0000000000..d681b1b1ac --- /dev/null +++ b/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Actions.js @@ -0,0 +1,14 @@ +import {Badge} from "antd"; +import Link from "next/link"; +import {environmentsUri} from "@components/extension/environments/EnvironmentsLinksUtils"; + +export default function EnvironmentBuildCountBuildPromotionInfoItemActions({item, build, onChange}) { + return ( + <> + {/* TODO https://trello.com/c/LYl0s5dH/151-environment-page-as-control-board */} + + + + + ) +} \ No newline at end of file diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Dot.js b/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Dot.js similarity index 57% rename from ontrack-web-core/components/framework/build-promotion-info-item/Slot/Dot.js rename to ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Dot.js index faf83e0779..e3f7ec37ca 100644 --- a/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Dot.js +++ b/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Dot.js @@ -1,6 +1,6 @@ import {FaServer} from "react-icons/fa"; -export default function SlotBuildPromotionInfoItemDot({item, build, onChange}) { +export default function EnvironmentBuildCountBuildPromotionInfoItemDot({item, build, onChange}) { return ( <> diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Name.js b/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Name.js new file mode 100644 index 0000000000..921327de02 --- /dev/null +++ b/ontrack-web-core/components/framework/build-promotion-info-item/EnvironmentBuildCount/Name.js @@ -0,0 +1,15 @@ +import {environmentsUri} from "@components/extension/environments/EnvironmentsLinksUtils"; +import Link from "next/link"; + +export default function EnvironmentBuildCountBuildPromotionInfoItemName({item, build, onChange}) { + return ( + <> + + # of environments + + + ) +} \ No newline at end of file diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Actions.js b/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Actions.js deleted file mode 100644 index ce1fe7f692..0000000000 --- a/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Actions.js +++ /dev/null @@ -1,75 +0,0 @@ -import {Space} from "antd"; -import SlotPipelineCreateButton from "@components/extension/environments/SlotPipelineCreateButton"; -import {useGraphQLClient} from "@components/providers/ConnectionContextProvider"; -import {useEffect, useState} from "react"; -import {gql} from "graphql-request"; -import BuildLink from "@components/builds/BuildLink"; -import SlotPipelineStatus from "@components/extension/environments/SlotPipelineStatus"; -import {slotPipelineUri} from "@components/extension/environments/EnvironmentsLinksUtils"; -import Link from "next/link"; -import LoadingInline from "@components/common/LoadingInline"; - -export default function SlotBuildPromotionInfoItemActions({item, build, onChange}) { - - const client = useGraphQLClient() - const [loading, setLoading] = useState(true) - const [pipeline, setPipeline] = useState() - - useEffect(() => { - if (client) { - setLoading(true) - client.request( - gql` - query SlotCurrentPipeline($slotId: String!) { - slotById(id: $slotId) { - currentPipeline { - id - status - build { - id - name - releaseProperty { - value - } - } - } - } - } - `, - { - slotId: item.id - } - ).then(data => { - setPipeline(data.slotById.currentPipeline) - }).finally(() => { - setLoading(false) - }) - } - }, [client, item.id]) - - return ( - <> - - - {/* Current build & status */} - - { - pipeline && - - - - - - - } - - - - ) -} \ No newline at end of file diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Name.js b/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Name.js deleted file mode 100644 index 78be23318b..0000000000 --- a/ontrack-web-core/components/framework/build-promotion-info-item/Slot/Name.js +++ /dev/null @@ -1,12 +0,0 @@ -import {slotUri} from "@components/extension/environments/EnvironmentsLinksUtils"; -import Link from "next/link"; - -export default function SlotBuildPromotionInfoItemName({item, build, onChange}) { - return ( - <> - - {item.environment.name}{item.qualifier && `/${item.qualifier}`} - - - ) -} \ No newline at end of file diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Actions.js b/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Actions.js index b718e4c3a8..84c87be0fa 100644 --- a/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Actions.js +++ b/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Actions.js @@ -1,13 +1,14 @@ -import SlotPipelineStatusActions from "@components/extension/environments/SlotPipelineStatusActions"; +import {environmentsUri} from "@components/extension/environments/EnvironmentsLinksUtils"; +import Link from "next/link"; +import TimestampText from "@components/common/TimestampText"; +import React from "react"; export default function SlotPipelineBuildPromotionInfoItemActions({item, onChange}) { return ( <> - + + + ) } \ No newline at end of file diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Dot.js b/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Dot.js index 497aa62dd1..4c3cf2d9bb 100644 --- a/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Dot.js +++ b/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Dot.js @@ -1,5 +1,5 @@ -import {FaServer} from "react-icons/fa"; +import EnvironmentIcon from "@components/extension/environments/EnvironmentIcon"; -export default function SlotPipelineBuildPromotionInfoItemDot(props) { - return +export default function SlotPipelineBuildPromotionInfoItemDot({item}) { + return } \ No newline at end of file diff --git a/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Name.js b/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Name.js index 65bac64435..be29228fa7 100644 --- a/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Name.js +++ b/ontrack-web-core/components/framework/build-promotion-info-item/SlotPipeline/Name.js @@ -1,11 +1,12 @@ -import {slotPipelineUri} from "@components/extension/environments/EnvironmentsLinksUtils"; import Link from "next/link"; +import {environmentsUri} from "@components/extension/environments/EnvironmentsLinksUtils"; export default function SlotPipelineBuildPromotionInfoItemActions({item, build, promotionLevel, onChange}) { return ( <> - - {item.slot.environment.name}{item.slot.qualifier && `/${item.slot.qualifier}`} #{item.number} + {/* TODO https://trello.com/c/LYl0s5dH/151-environment-page-as-control-board */} + + {item.slot.environment.name}{item.slot.qualifier && `/${item.slot.qualifier}`}   ) diff --git a/ontrack-web-core/components/framework/settings/environments-form.js b/ontrack-web-core/components/framework/settings/environments-form.js new file mode 100644 index 0000000000..b6414be291 --- /dev/null +++ b/ontrack-web-core/components/framework/settings/environments-form.js @@ -0,0 +1,25 @@ +import {Form, Select} from "antd"; +import SettingsForm from "@components/core/admin/settings/SettingsForm"; + +export default function EnvironmentsForm({id, ...values}) { + + // See EnvironmentsSettingsBuildDisplayOption + const options = [ + {value: 'ALL', label: 'All environments must be displayed'}, + {value: 'HIGHEST', label: 'Only the highest environment is displayed'}, + {value: 'COUNT', label: 'Only a count of the environments is displayed'}, + ] + + return ( + <> + + +