Skip to content

Commit

Permalink
feat: toggle logging house status (#261)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilczaja authored Aug 8, 2024
1 parent a64a168 commit 0306ec9
Show file tree
Hide file tree
Showing 24 changed files with 108 additions and 50 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md).
- Fixed provider organization ID not showing up on CaaS connectors [#206](https://github.com/sovity/authority-portal/issues/206)
- Keep in mind that sovity needs to be registered in the portal for the ID to show up.
- Already registered connectors will be updated automatically, this process can take up to 24 hours
- Added a message when the CaaS request feature is not available
- Catalog: Removed dataspace filter when only one dataspace is known

### Known issues

Expand All @@ -39,15 +41,22 @@ Environment variable changes:
authority-portal.organization.id.prefix: "MDS"
authority-portal.organization.id.length: "4"
```
- New **mandatory** configuration variables:
- ```yaml
# Enables the client to connect to the CaaS service. If you weren't provided credentials for the feature by sovity, set this to false
quarkus.oidc-client.sovity.client-enabled: true
```
#### Frontend
Environment variable changes:
- New optional configuration variables - the values assigned here are the ones you should use to retain the current behavior:
- New **mandantory** configuration variables - the values assigned here are the ones you should use to retain the current behavior:
- ```yaml
# UI Branding profile
AUTHORITY_PORTAL_FRONTEND_ACTIVE_PROFILE: mds-open-source
# Short Dataspace name, used in some explanatory texts
AUTHORITY_PORTAL_FRONTEND_DATASPACE_SHORT_NAME: MDS
# Portal name displayed in various texts
AUTHORITY_PORTAL_FRONTEND_PORTAL_DISPLAY_NAME: "MDS Portal"
```
#### Compatible Versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ data class DeploymentEnvironmentDto(
val dapsTokenUrl: String,
@field:Schema(description = "DAPS JWKS URL", requiredMode = Schema.RequiredMode.REQUIRED)
val dapsJwksUrl: String,
@field:Schema(description = "Logging House URL", requiredMode = Schema.RequiredMode.REQUIRED)
val loggingHouseUrl: String,
@field:Schema(description = "Logging House URL", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
val loggingHouseUrl: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ import de.sovity.authorityportal.broker.dao.pages.catalog.CatalogQueryFields
import de.sovity.authorityportal.broker.dao.pages.catalog.models.CatalogQueryFilter
import de.sovity.authorityportal.broker.dao.pages.catalog.models.CatalogQuerySelectedFilterQuery
import de.sovity.authorityportal.broker.dao.utils.JsonDeserializationUtils.read2dStringList
import de.sovity.authorityportal.web.environment.CatalogDataspaceConfigService
import de.sovity.authorityportal.web.environment.DeploymentEnvironmentService
import jakarta.enterprise.context.ApplicationScoped
import org.eclipse.microprofile.config.inject.ConfigProperty

@ApplicationScoped
class CatalogFilterService(
val catalogFilterAttributeDefinitionService: CatalogFilterAttributeDefinitionService
val catalogFilterAttributeDefinitionService: CatalogFilterAttributeDefinitionService,
val deploymentEnvironmentService: DeploymentEnvironmentService,
val catalogDataspaceConfigService: CatalogDataspaceConfigService
) {

private val caseInsensitiveEmptyStringLast = Comparator<String> { s1, s2 ->
Expand All @@ -42,13 +47,13 @@ class CatalogFilterService(
*
* @return attribute definitions
*/
get() = listOf(
get() = listOfNotNull(
catalogFilterAttributeDefinitionService.forField(
{ fields: CatalogQueryFields -> fields.dataSourceAvailabilityLabel },
"dataSourceAvailability",
"Data Offer Type"
),
catalogFilterAttributeDefinitionService.buildDataSpaceFilter(),
catalogFilterAttributeDefinitionService.buildDataSpaceFilter().takeIf { this.shouldSupportMultipleDataspaces() },
catalogFilterAttributeDefinitionService.forField(
{ fields: CatalogQueryFields -> fields.dataOfferTable.DATA_CATEGORY },
"dataCategory",
Expand Down Expand Up @@ -166,4 +171,13 @@ class CatalogFilterService(
.filter { it.selectedIds.isNotEmpty() }
.associate { it.id to it.selectedIds }
}

private fun shouldSupportMultipleDataspaces(): Boolean {
deploymentEnvironmentService.findAll().forEach {
if (catalogDataspaceConfigService.forEnvironment(it.key).namesByConnectorId.isNotEmpty()) {
return true
}
}
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ class UiResourceImpl(
override fun checkFreeCaasUsage(environmentId: String): CaasAvailabilityResponse {
authUtils.requiresRole(Roles.UserRoles.PARTICIPANT_CURATOR)
authUtils.requiresMemberOfAnyOrganization()
return caasManagementApiService.getFreeCaasUsageForOrganization(loggedInUser.organizationId!!, environmentId)
return caasManagementApiService.getCaasAvailabilityForOrganization(loggedInUser.organizationId!!, environmentId)
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package de.sovity.authorityportal.web.environment

import io.smallrye.config.ConfigMapping
import java.time.Duration
import java.util.Optional

@ConfigMapping(prefix = "authority-portal.deployment")
interface DeploymentEnvironmentConfiguration {
Expand All @@ -25,7 +26,7 @@ interface DeploymentEnvironmentConfiguration {
fun position(): Int
fun daps(): DapsConfig
fun dataCatalog(): DataCatalogConfig
fun loggingHouse(): LoggingHouseConfig
fun loggingHouse(): Optional<LoggingHouseConfig>

interface DapsConfig {
fun url(): String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import de.sovity.authorityportal.web.environment.DeploymentEnvironmentConfigurat
import de.sovity.authorityportal.web.environment.DeploymentEnvironmentConfiguration.DeploymentEnvironment.DapsConfig
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import kotlin.jvm.optionals.getOrNull

@ApplicationScoped
class DeploymentEnvironmentDtoService {

@Inject
lateinit var deploymentEnvironmentService: DeploymentEnvironmentService
class DeploymentEnvironmentDtoService(
val deploymentEnvironmentService: DeploymentEnvironmentService
) {

fun findByIdOrThrow(envId: String): DeploymentEnvironmentDto =
buildDto(envId, deploymentEnvironmentService.findByIdOrThrow(envId))
Expand All @@ -41,7 +41,7 @@ class DeploymentEnvironmentDtoService {
title = deploymentEnvironment.title(),
dapsJwksUrl = buildDapsJwksUrl(deploymentEnvironment.daps()),
dapsTokenUrl = buildDapsTokenUrl(deploymentEnvironment.daps()),
loggingHouseUrl = deploymentEnvironment.loggingHouse().url()
loggingHouseUrl = deploymentEnvironment.loggingHouse().getOrNull()?.url()
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import de.sovity.authorityportal.web.utils.idmanagement.DataspaceComponentIdUtil
import io.quarkus.logging.Log
import jakarta.enterprise.context.ApplicationScoped
import org.eclipse.microprofile.config.inject.ConfigProperty
import kotlin.jvm.optionals.getOrNull

@ApplicationScoped
class CaasManagementApiService(
Expand All @@ -43,7 +44,8 @@ class CaasManagementApiService(
val clientIdUtils: ClientIdUtils,
val userService: UserService,
val timeUtils: TimeUtils,
@ConfigProperty(name = "authority-portal.caas.sovity.limit-per-organization") val caasLimitPerOrganizationId: String
@ConfigProperty(name = "authority-portal.caas.sovity.limit-per-organization") val caasLimitPerOrganizationId: String,
@ConfigProperty(name = "quarkus.oidc-client.sovity.client-enabled") val isCaasClientEnabled: Boolean
) {

fun createCaas(
Expand Down Expand Up @@ -89,7 +91,11 @@ class CaasManagementApiService(
return CreateConnectorResponse.ok(connectorId, timeUtils.now())
}

fun getFreeCaasUsageForOrganization(organizationId: String, environmentId: String): CaasAvailabilityResponse {
fun getCaasAvailabilityForOrganization(organizationId: String, environmentId: String): CaasAvailabilityResponse {
if (!isCaasClientEnabled) {
return CaasAvailabilityResponse(0, 0)
}

val caasLimit = caasLimitPerOrganizationId.toInt()
val caasCount = connectorService.getCaasCountByOrganizationIdAndEnvironment(organizationId, environmentId)

Expand Down Expand Up @@ -130,7 +136,7 @@ class CaasManagementApiService(
connectorDescription = caasRequest.connectorDescription.trim(),
participantOrganizationUrl = curatorOrganization.url,
participantOrganizationLegalName = curatorOrganization.name,
clearingHouseUrl = deploymentEnvironmentService.findByIdOrThrow(environmentId).loggingHouse().url(),
clearingHouseUrl = deploymentEnvironmentService.findByIdOrThrow(environmentId).loggingHouse().getOrNull()?.url(),
brokerUrl = "https://this-field-is-deprecated",
dapsTokenUrl = buildDapsTokenUrl(environmentId),
dapsJwksUrl = buildDapsJwksUrl(environmentId),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,16 @@ import de.sovity.authorityportal.web.thirdparty.uptimekuma.model.ComponentStatus
import de.sovity.authorityportal.web.thirdparty.uptimekuma.model.ComponentStatusOverview
import io.quarkus.logging.Log
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import org.eclipse.microprofile.config.inject.ConfigProperty
import java.util.Base64
import kotlin.jvm.optionals.getOrNull

@ApplicationScoped
class UptimeKumaClient {

@Inject
lateinit var deploymentEnvironmentService: DeploymentEnvironmentService

@Inject
lateinit var uptimeKumaClientResource: UptimeKumaClientResource

@ConfigProperty(name = "authority-portal.kuma.api-key")
lateinit var uptimeKumaApiKey: String

class UptimeKumaClient(
val deploymentEnvironmentService: DeploymentEnvironmentService,
val uptimeKumaClientResource: UptimeKumaClientResource,
@ConfigProperty(name = "authority-portal.kuma.api-key") val uptimeKumaApiKey: String,
) {

fun getStatusByEnvironments(): Map<String, ComponentStatusOverview> {
val basicAuthHeader = Base64.getEncoder().encodeToString(":$uptimeKumaApiKey".toByteArray())
Expand All @@ -44,11 +38,17 @@ class UptimeKumaClient {
return environments.mapValues { getComponentStatusOverview(response, it.value) }
}

private fun getComponentStatusOverview(response: String, envConfig: DeploymentEnvironment): ComponentStatusOverview =
private fun getComponentStatusOverview(
response: String,
envConfig: DeploymentEnvironment
): ComponentStatusOverview =
ComponentStatusOverview().also {
it.daps = getComponentStatus(envConfig.daps().kumaName(), response)
it.loggingHouse = getComponentStatus(envConfig.loggingHouse().kumaName(), response)
it.catalogCrawler = getComponentStatus(envConfig.dataCatalog().kumaName(), response)
it.loggingHouse = getComponentStatus(
envConfig.loggingHouse().get().kumaName(),
response
).takeUnless { envConfig.loggingHouse().getOrNull()?.kumaName().isNullOrBlank() }
}

private fun getComponentStatus(componentName: String, response: String): ComponentStatus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import io.swagger.v3.oas.annotations.media.Schema
class ComponentStatusOverview {
@Schema(description = "DAPS status", requiredMode = Schema.RequiredMode.REQUIRED)
var daps: ComponentStatus? = null
@Schema(description = "Logging House status", requiredMode = Schema.RequiredMode.REQUIRED)
var loggingHouse: ComponentStatus? = null
@Schema(description = "Catalog crawler (Broker) status", requiredMode = Schema.RequiredMode.REQUIRED)
var catalogCrawler: ComponentStatus? = null
@Schema(description = "Logging House status", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
var loggingHouse: ComponentStatus? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ quarkus.oidc-client.sovity.early-tokens-acquisition=false
%test.quarkus.oidc-client.sovity.credentials.secret=secret
%test.quarkus.oidc-client.sovity.client-enabled=false

# Defaults so that the AP can actually start without the Customer Portal
quarkus.oidc-client.sovity.client-enabled=false

# Authority Portal Configuration
%dev.authority-portal.base-url=https://localhost:4200
%dev.authority-portal.config.api-key=secret
Expand Down Expand Up @@ -137,8 +140,7 @@ quarkus.oidc-client.sovity.early-tokens-acquisition=false
%dev.authority-portal.deployment.environments.dev.daps.client-id=client
%dev.authority-portal.deployment.environments.dev.daps.client-secret=secret
%dev.authority-portal.deployment.environments.dev.daps.kuma-name="DEV mds daps"
%dev.authority-portal.deployment.environments.dev.logging-house.url=https://url
%dev.authority-portal.deployment.environments.dev.logging-house.kuma-name="DEV mds logging-house"
%dev.authority-portal.deployment.environments.dev.data-catalog.dataspace-names.connector-ids.\"MDSL1234XX.C1234XX\"="Mobilithek"

# Test properties
%test.authority-portal.deployment.environments.test.title=Test Environment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ class CatalogApiTest {
// assert
assertThat(result.availableFilters.fields).allSatisfy {
it.id in setOf(
"dataSpace",
"dataCategory",
"dataSubcategory",
"dataModel",
Expand All @@ -382,7 +381,6 @@ class CatalogApiTest {

assertThat(result.availableFilters.fields).allSatisfy {
it.title in setOf(
"Data Space",
"Data Category",
"Data Subcategory",
"Data Model",
Expand All @@ -395,9 +393,6 @@ class CatalogApiTest {
)
}

val dataSpace = getAvailableFilter(result, "dataSpace")
assertThat(dataSpace.values).allSatisfy { it.id in setOf("MDS") }

val dataCategory = getAvailableFilter(result, "dataCategory")
assertThat(dataCategory.values).allSatisfy { it.id in setOf("Data Category 1") }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ class CaasManagementApiServiceTest {

// assert
assertThat(result).isNotNull
assertThat(result.limit).isEqualTo(1)
assertThat(result.current).isEqualTo(1)

// This will always return 0/0 because the function checks if the OIDC Client is enabled.
// Unfortunately, the OIDC Client is not enabled in the test environment because Quarkus would refuse to start.
assertThat(result.limit).isEqualTo(0)
assertThat(result.current).isEqualTo(0)
}
}
1 change: 1 addition & 0 deletions authority-portal-frontend/.env.local-dev
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ AUTHORITY_PORTAL_FRONTEND_LEGAL_NOTICE_URL=https://legal-notice-url
AUTHORITY_PORTAL_FRONTEND_SUPPORT_URL=https://support-url
AUTHORITY_PORTAL_FRONTEND_ACTIVE_PROFILE=sovity-open-source
AUTHORITY_PORTAL_FRONTEND_DATASPACE_SHORT_NAME=sovity Dataspace
AUTHORITY_PORTAL_FRONTEND_PORTAL_DISPLAY_NAME=Authority Portal
1 change: 1 addition & 0 deletions authority-portal-frontend/.env.local-e2e-dev
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ AUTHORITY_PORTAL_FRONTEND_LEGAL_NOTICE_URL=https://mobility-dataspace.eu/legal-n
AUTHORITY_PORTAL_FRONTEND_SUPPORT_URL=https://support.mobility-dataspace.eu
AUTHORITY_PORTAL_FRONTEND_ACTIVE_PROFILE=sovity-open-source
AUTHORITY_PORTAL_FRONTEND_BRAND_SHORT_NAME=sovity
AUTHORITY_PORTAL_FRONTEND_PORTAL_DISPLAY_NAME=Authority Portal
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface AppConfig {
useFakeBackend: boolean;
useLocalBackend: boolean;
brandDataspaceName: string;
portalName: string;
}

/**
Expand All @@ -76,6 +77,7 @@ export interface AppConfigEnv {
AUTHORITY_PORTAL_FRONTEND_SUPPORT_URL: string;
AUTHORITY_PORTAL_FRONTEND_ACTIVE_PROFILE: string;
AUTHORITY_PORTAL_FRONTEND_DATASPACE_SHORT_NAME: string;
AUTHORITY_PORTAL_FRONTEND_PORTAL_DISPLAY_NAME: string;
}

/**
Expand Down Expand Up @@ -106,5 +108,6 @@ export function buildAppConfig(envVars: AppConfigEnv): AppConfig {
legalNoticeUrl: envVars.AUTHORITY_PORTAL_FRONTEND_LEGAL_NOTICE_URL,
supportUrl: envVars.AUTHORITY_PORTAL_FRONTEND_SUPPORT_URL,
brandDataspaceName: envVars.AUTHORITY_PORTAL_FRONTEND_DATASPACE_SHORT_NAME,
portalName: envVars.AUTHORITY_PORTAL_FRONTEND_PORTAL_DISPLAY_NAME,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export const MDS_FEATURES: UiFeature[] = [
'catalogue',
'iframe-in-home',
'enable-home',
'uses-logging-house',
];
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ export type UiFeature =
| 'catalogue'

// Enables the 'Powered by sovity' footer to be used in instances hosted by sovity
| 'powered-by-sovity';
| 'powered-by-sovity'

// Enables the Dashboard to display the Logging House status
| 'uses-logging-house';
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@
<h2 class="selection-page-title max-w-xl mx-auto">
Request a Connector-as-a-Service
</h2>
<p class="selection-page-prose">
<p *ngIf="sponsoredCaasAmount > 0" class="selection-page-prose">
As {{ inferArticle(this.appConfig.brandDataspaceName) }}
{{ this.appConfig.brandDataspaceName }} member, you have the option to
request {{ this.sponsoredCaasAmount }} free CaaS per environment.
</p>
<!-- CaaS feature not configured -->
<p *ngIf="sponsoredCaasAmount == 0" class="selection-page-prose">
Your dataspace authority has not configured this feature yet. Contact
<a href="mailto:[email protected]">sovity</a> to purchase an individual
CaaS subscription.
</p>
<!-- END CaaS feature not configured -->
<div class="flex gap-4 flex-row mt-8 justify-evenly w-full">
<app-selection-box
[selectionBoxConfig]="selectionBox"></app-selection-box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,17 @@ export class ChooseParticipantCaasComponent implements OnInit, OnDestroy {

this.sponsoredCaasAmount = x.limit;
const isLimitReached = x.current >= x.limit;
const isUnconfigured = x.limit == 0;

this.selectionBox.action = {
label: `Request CaaS (${x.current}/${x.limit})`,
label: isUnconfigured
? 'Unavailable'
: `Request CaaS (${x.current}/${x.limit})`,
url: 'my-organization/connectors/new/provided',
isDisabled: isLimitReached,
hint: isLimitReached
isDisabled: isLimitReached || isUnconfigured,
hint: isUnconfigured
? 'Your dataspace authority has not configured this feature'
: isLimitReached
? 'The existing CaaS connector needs to be removed before requesting a new one'
: '',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
label="DAPS"
[data]="dapsData"></app-dashboard-component-uptime-card>
<app-dashboard-component-uptime-card
*ngIf="'uses-logging-house' | isActiveFeature"
label="Logging House"
[data]="loggingHouseData"></app-dashboard-component-uptime-card>
<app-dashboard-component-uptime-card
Expand Down
Loading

0 comments on commit 0306ec9

Please sign in to comment.