Skip to content

Commit

Permalink
feat: Unify various API endpoints related to config
Browse files Browse the repository at this point in the history
More details in the linked issue.

Closes #1960
  • Loading branch information
zusorio committed Dec 2, 2024
1 parent f4b33e5 commit a21f882
Show file tree
Hide file tree
Showing 33 changed files with 291 additions and 523 deletions.
16 changes: 0 additions & 16 deletions backend/capellacollab/feedback/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
import fastapi
from sqlalchemy import orm

from capellacollab.config import config
from capellacollab.core import database
from capellacollab.core import logging as log
from capellacollab.core.authentication import injectables as auth_injectables
from capellacollab.settings.configuration import core as config_core
from capellacollab.settings.configuration import models as config_models
from capellacollab.users import injectables as user_injectables
from capellacollab.users import models as users_models

Expand All @@ -23,19 +20,6 @@
router = fastapi.APIRouter()


@router.get(
"/configurations/feedback",
response_model=config_models.FeedbackConfiguration,
)
def get_feedback_configuration(
db: orm.Session = fastapi.Depends(database.get_db),
):
feedback = config_core.get_global_configuration(db).feedback
if not (config.smtp and config.smtp.enabled):
util.disable_feedback(feedback)
return feedback


@router.post(
"/feedback",
status_code=204,
Expand Down
45 changes: 0 additions & 45 deletions backend/capellacollab/metadata/routes.py

This file was deleted.

23 changes: 0 additions & 23 deletions backend/capellacollab/navbar/routes.py

This file was deleted.

6 changes: 2 additions & 4 deletions backend/capellacollab/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
from capellacollab.events import routes as events_router
from capellacollab.feedback import routes as feedback_routes
from capellacollab.health import routes as health_routes
from capellacollab.metadata import routes as core_metadata
from capellacollab.navbar import routes as navbar_routes
from capellacollab.notices import routes as notices_routes
from capellacollab.projects import routes as projects_routes
from capellacollab.sessions import routes as sessions_routes
from capellacollab.settings import routes as settings_routes
from capellacollab.tools import routes as tools_routes
from capellacollab.unifiedconfig import routes as unified_config_routes
from capellacollab.users import routes as users_routes

log = logging.getLogger(__name__)
Expand All @@ -30,9 +29,8 @@
responses=auth_responses.api_exceptions(include_authentication=True),
tags=["Health"],
)
router.include_router(core_metadata.router, tags=["Metadata"])
router.include_router(navbar_routes.router, tags=["Navbar"])
router.include_router(feedback_routes.router, tags=["Feedback"])
router.include_router(unified_config_routes.router, tags=["Unified Config"])
router.include_router(
sessions_routes.router,
prefix="/sessions",
Expand Down
2 changes: 2 additions & 0 deletions backend/capellacollab/unifiedconfig/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
25 changes: 25 additions & 0 deletions backend/capellacollab/unifiedconfig/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from capellacollab.core import pydantic as core_pydantic
from capellacollab.settings.configuration import models as config_models


class Metadata(core_pydantic.BaseModel):
version: str
privacy_policy_url: str | None
imprint_url: str | None
provider: str | None
authentication_provider: str | None
environment: str | None

host: str | None
port: str | None
protocol: str | None


class UnifiedConfig(core_pydantic.BaseModel):
metadata: Metadata
feedback: config_models.FeedbackConfiguration
navbar: config_models.NavbarConfiguration
beta: config_models.BetaConfiguration
27 changes: 27 additions & 0 deletions backend/capellacollab/unifiedconfig/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import fastapi
from sqlalchemy import orm

from capellacollab.core import database
from capellacollab.settings.configuration import core as config_core
from capellacollab.unifiedconfig import models, util

router = fastapi.APIRouter()


@router.get(
"/unified-configuration",
)
def get_unified_config(
db: orm.Session = fastapi.Depends(database.get_db),
) -> models.UnifiedConfig:
cfg = config_core.get_global_configuration(db)

return models.UnifiedConfig(
metadata=util.get_metadata(cfg),
feedback=util.get_feedback(cfg),
beta=util.get_beta(cfg),
navbar=util.get_navbar(cfg),
)
47 changes: 47 additions & 0 deletions backend/capellacollab/unifiedconfig/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import capellacollab
from capellacollab.config import config
from capellacollab.settings.configuration import models as config_models
from capellacollab.unifiedconfig import models


def get_metadata(
global_config: config_models.GlobalConfiguration,
) -> models.Metadata:
return models.Metadata.model_validate(
global_config.metadata.model_dump()
| {
"version": capellacollab.__version__,
"host": config.general.host,
"port": str(config.general.port),
"protocol": config.general.scheme,
}
)


def get_feedback(
global_config: config_models.GlobalConfiguration,
) -> config_models.FeedbackConfiguration:
feedback = global_config.feedback
if not (config.smtp and config.smtp.enabled):
feedback.enabled = False
feedback.after_session = False
feedback.on_footer = False
feedback.on_session_card = False
feedback.interval.enabled = False

return feedback


def get_navbar(
global_config: config_models.GlobalConfiguration,
) -> config_models.NavbarConfiguration:
return global_config.navbar


def get_beta(
global_config: config_models.GlobalConfiguration,
) -> config_models.BetaConfiguration:
return global_config.beta
13 changes: 0 additions & 13 deletions backend/capellacollab/users/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from capellacollab.projects.users import crud as projects_users_crud
from capellacollab.sessions import routes as session_routes
from capellacollab.settings.configuration import core as config_core
from capellacollab.settings.configuration import models as config_models
from capellacollab.users import injectables as users_injectables
from capellacollab.users import models as users_models
from capellacollab.users.tokens import routes as tokens_routes
Expand All @@ -41,18 +40,6 @@ def get_current_user(
return user


@router.get(
"/beta",
response_model=config_models.BetaConfiguration,
)
def get_beta_config(db: orm.Session = fastapi.Depends(database.get_db)):
cfg = config_core.get_global_configuration(db)

return config_models.BetaConfiguration.model_validate(
cfg.beta.model_dump()
)


@router.get(
"/{user_id}",
response_model=models.User,
Expand Down
8 changes: 4 additions & 4 deletions backend/tests/settings/test_global_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ def test_metadata_is_updated(

assert response.status_code == 200

response = client.get("/api/v1/metadata")
response = client.get("/api/v1/unified-configuration")
assert response.status_code == 200
assert response.json()["environment"] == "test"
assert response.json()["metadata"]["environment"] == "test"


@pytest.mark.usefixtures("admin")
Expand All @@ -154,9 +154,9 @@ def test_navbar_is_updated(

assert response.status_code == 200

response = client.get("/api/v1/navbar")
response = client.get("/api/v1/unified-configuration")
assert response.status_code == 200
assert response.json()["external_links"][0] == {
assert response.json()["navbar"]["external_links"][0] == {
"name": "Example",
"href": "https://example.com",
"role": "user",
Expand Down
8 changes: 4 additions & 4 deletions backend/tests/test_feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ def test_feedback_is_updated(

assert response.status_code == 200

response = client.get("/api/v1/configurations/feedback")
response = client.get("/api/v1/unified-configuration")
assert response.status_code == 200
assert response.json() == {
assert response.json()["feedback"] == {
"enabled": True,
"after_session": True,
"on_footer": True,
Expand Down Expand Up @@ -262,9 +262,9 @@ def test_activate_feedback_without_recipients(

@pytest.mark.usefixtures("user", "smtp_config_not_set", "feedback_enabled")
def test_feedback_is_disabled_without_smtp(client: testclient.TestClient):
response = client.get("/api/v1/configurations/feedback")
response = client.get("/api/v1/unified-configuration")
assert response.status_code == 200
assert response.json()["enabled"] is False
assert response.json()["feedback"]["enabled"] is False


def test_feedback_metric(db: orm.Session):
Expand Down
20 changes: 3 additions & 17 deletions frontend/src/app/general/metadata/metadata.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import {
Metadata,
MetadataService as OpenAPIMetadataService,
} from 'src/app/openapi';
import { UnifiedConfigWrapperService } from '../../services/unified-config-wrapper/unified-config-wrapper.service';

@Injectable({
providedIn: 'root',
Expand All @@ -18,19 +14,15 @@ export class MetadataService {
constructor(
private httpClient: HttpClient,
public dialog: MatDialog,
private metadataService: OpenAPIMetadataService,
private unifiedConfigWrapperService: UnifiedConfigWrapperService,
) {
this.loadVersion();
this.loadBackendMetadata().subscribe();
}

public version: Version | undefined;
public oldVersion: string | undefined;

private _backendMetadata = new BehaviorSubject<Metadata | undefined>(
undefined,
);
readonly backendMetadata = this._backendMetadata.asObservable();
readonly backendMetadata = this.unifiedConfigWrapperService.metadata$;

loadVersion(): void {
if (!this.version) {
Expand All @@ -42,12 +34,6 @@ export class MetadataService {
}
}

loadBackendMetadata(): Observable<Metadata> {
return this.metadataService
.getMetadata()
.pipe(tap((metadata: Metadata) => this._backendMetadata.next(metadata)));
}

get changedVersion(): boolean {
const currentVersion = localStorage.getItem('version');

Expand Down
28 changes: 7 additions & 21 deletions frontend/src/app/general/nav-bar/nav-bar.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,20 @@
*/
import { Injectable } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { BehaviorSubject, map, Observable, tap } from 'rxjs';
import {
BuiltInLinkItem,
NavbarConfigurationOutput,
NavbarService as OpenAPINavbarService,
Role,
} from 'src/app/openapi';
import { map } from 'rxjs';
import { BuiltInLinkItem, Role } from 'src/app/openapi';
import { environment } from 'src/environments/environment';
import { UnifiedConfigWrapperService } from '../../services/unified-config-wrapper/unified-config-wrapper.service';

@Injectable({
providedIn: 'root',
})
export class NavBarService {
constructor(private navbarService: OpenAPINavbarService) {
this.loadNavbarConfig().subscribe();
}

loadNavbarConfig(): Observable<NavbarConfigurationOutput> {
return this.navbarService
.getNavbar()
.pipe(tap((navConf) => this._navbarConfig.next(navConf)));
}

private _navbarConfig = new BehaviorSubject<
NavbarConfigurationOutput | undefined
>(undefined);
constructor(
private unifiedConfigWrapperService: UnifiedConfigWrapperService,
) {}

readonly navbarItems$ = this._navbarConfig.pipe(
readonly navbarItems$ = this.unifiedConfigWrapperService.navbar$.pipe(
map(
(navbarConfig): NavBarItem[] =>
navbarConfig?.external_links.map((link) => ({
Expand Down
Loading

0 comments on commit a21f882

Please sign in to comment.