Skip to content

Commit

Permalink
feat: Add indicator to warn when most t4c licenses are being used
Browse files Browse the repository at this point in the history
Closes #1784
  • Loading branch information
zusorio committed Oct 23, 2024
1 parent 7963611 commit 34bb4f3
Show file tree
Hide file tree
Showing 11 changed files with 378 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,18 @@ class T4CLicenseServer(T4CLicenseServerBase):
usage: interface.T4CLicenseServerUsage | None = None
warnings: list[core_models.Message] = []
instances: list[t4c_instance_models2.SimpleT4CInstance] = []
anonymized: bool = pydantic.Field(default=False, exclude=True)

def anonymize(self):
self.usage_api = ""
self.license_key = ""
self.anonymized = True

Check warning on line 73 in backend/capellacollab/settings/modelsources/t4c/license_server/models.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/settings/modelsources/t4c/license_server/models.py#L71-L73

Added lines #L71 - L73 were not covered by tests

@pydantic.model_validator(mode="after")
def add_from_api(self) -> t.Any:
if self.anonymized:
return self

Check warning on line 78 in backend/capellacollab/settings/modelsources/t4c/license_server/models.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/settings/modelsources/t4c/license_server/models.py#L78

Added line #L78 was not covered by tests

self.license_server_version = interface.get_t4c_license_server_version(
self.usage_api
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from collections import abc

import fastapi
from sqlalchemy import orm

from capellacollab.core import database
from capellacollab.core.authentication import injectables as auth_injectables
from capellacollab.users import models as users_models
from capellacollab.users import injectables as users_injectables

from . import crud, exceptions, injectables, interface, models

Expand All @@ -25,11 +24,23 @@
router = fastapi.APIRouter()


@admin_router.get("", response_model=list[models.T4CLicenseServer])
@router.get("")
def get_t4c_license_servers(
db: orm.Session = fastapi.Depends(database.get_db),
) -> abc.Sequence[models.DatabaseT4CLicenseServer]:
return crud.get_t4c_license_servers(db)
user: users_models.DatabaseUser = fastapi.Depends(
users_injectables.get_own_user
),
) -> list[models.T4CLicenseServer]:
license_servers = [
models.T4CLicenseServer.model_validate(license_server)
for license_server in crud.get_t4c_license_servers(db)
]

if user.role != users_models.Role.ADMIN:
for license_server in license_servers:
license_server.anonymize()

Check warning on line 41 in backend/capellacollab/settings/modelsources/t4c/license_server/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/settings/modelsources/t4c/license_server/routes.py#L41

Added line #L41 was not covered by tests

return license_servers


@admin_router.get(
Expand Down Expand Up @@ -99,7 +110,7 @@ def delete_t4c_license_server(
"/{t4c_license_server_id}/usage",
response_model=interface.T4CLicenseServerUsage,
)
def fetch_t4c_license_server_licenses(
def get_t4c_license_server_usage(
license_server: models.DatabaseT4CLicenseServer = fastapi.Depends(
injectables.get_existing_license_server
),
Expand Down
5 changes: 3 additions & 2 deletions backend/capellacollab/settings/modelsources/t4c/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
)

router.include_router(
settings_t4c_license_server_routes.admin_router,
settings_t4c_license_server_routes.router,
prefix="/license-servers",
tags=["Settings - Modelsources - T4C - License Servers"],
)


router.include_router(
settings_t4c_license_server_routes.router,
settings_t4c_license_server_routes.admin_router,
prefix="/license-servers",
tags=["Settings - Modelsources - T4C - License Servers"],
)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!--
~ SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

<div
class="max-h-0 overflow-hidden transition-all duration-700"
[ngClass]="{
'max-h-[100vh]':
((licenseUsageWrapperService.licenseServerUsage$ | async)?.length ?? -1) >
0,
}"
>
@if ((licenseUsageWrapperService.licenseServerUsage$ | async) !== undefined) {
@for (
licenseServer of licenseUsageWrapperService.licenseServerUsage$ | async;
track licenseServer.id
) {
@if (licenseServer.usage) {
<div
class="mt-2 flex items-center gap-2 rounded border p-2 text-sm shadow"
[class]="getLevel(licenseServer.usage)?.classes"
>
<mat-icon class="shrink-0">{{
getLevel(licenseServer.usage)?.icon
}}</mat-icon>
<span>{{ getLevel(licenseServer.usage)?.text }}</span>
</div>
}
}
}
</div>
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 { AsyncPipe, NgClass } from '@angular/common';
import { Component } from '@angular/core';
import { MatIcon } from '@angular/material/icon';
import { T4CLicenseServerUsage } from '../../openapi';
import { LicenseUsageWrapperService } from './license-usage.service';

@Component({
selector: 'app-license-indicator',
standalone: true,
imports: [MatIcon, AsyncPipe, NgClass],
templateUrl: './license-indicator.component.html',
})
export class LicenseIndicatorComponent {
constructor(public licenseUsageWrapperService: LicenseUsageWrapperService) {}

getLevel(usage: T4CLicenseServerUsage) {
const usagePercentage = (usage.total - usage.free) / usage.total;
return this.levels.find(
(level) => usagePercentage * 100 >= level.percentage,
);
}

levels = [
{
percentage: 100,
text: 'All TeamForCapella licenses are currently in use. You can start new sessions, but may encounter the error "Invalid license" when trying to use TeamForCapella. Please make sure to terminate your sessions after use.',
icon: 'error',
classes: 'bg-red-500 text-white',
},
{
percentage: 75,
text: 'Most TeamForCapella licenses are currently in use. Please make sure to terminate your sessions after use.',
icon: 'warning',
classes: 'bg-yellow-300',
},
{
percentage: 0,
text: 'TeamForCapella licenses are available. You can start new sessions without any issues.',
icon: 'check_circle',
classes: '',
},
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
* SPDX-License-Identifier: Apache-2.0
*/
import {
componentWrapperDecorator,
Meta,
moduleMetadata,
StoryObj,
} from '@storybook/angular';
import { BehaviorSubject } from 'rxjs';
import { T4CLicenseServer } from '../../openapi';
import { LicenseIndicatorComponent } from './license-indicator.component';
import { LicenseUsageWrapperService } from './license-usage.service';

const meta: Meta<LicenseIndicatorComponent> = {
title: 'Session Components/License Indicator',
component: LicenseIndicatorComponent,
decorators: [
componentWrapperDecorator(
(story) =>
`<div class="w-[336px] sm:w-[426px]">
${story}
</div>`,
),
],
};

export default meta;
type Story = StoryObj<LicenseIndicatorComponent>;

export class MockLicenseUsageWrapperService
implements Partial<LicenseUsageWrapperService>
{
private _licenseServerUsage = new BehaviorSubject<
T4CLicenseServer[] | undefined
>(undefined);
readonly licenseServerUsage$ = this._licenseServerUsage.asObservable();
constructor(licenseServerUsage: T4CLicenseServer[]) {
this._licenseServerUsage.next(licenseServerUsage);
}
}

export const AllUsed: Story = {
args: {},
decorators: [
moduleMetadata({
providers: [
{
provide: LicenseUsageWrapperService,
useFactory: () =>
new MockLicenseUsageWrapperService([
{
id: 1,
name: 'Test',
usage: {
free: 0,
total: 30,
},
license_server_version: '',
license_key: '',
usage_api: '',
instances: [],
warnings: [],
},
]),
},
],
}),
],
};

export const SomeUsed: Story = {
args: {},
decorators: [
moduleMetadata({
providers: [
{
provide: LicenseUsageWrapperService,
useFactory: () =>
new MockLicenseUsageWrapperService([
{
id: 1,
name: 'Test',
usage: {
free: 5,
total: 30,
},
license_server_version: '',
license_key: '',
usage_api: '',
instances: [],
warnings: [],
},
]),
},
],
}),
],
};

export const FewUsed: Story = {
args: {},
decorators: [
moduleMetadata({
providers: [
{
provide: LicenseUsageWrapperService,
useFactory: () =>
new MockLicenseUsageWrapperService([
{
id: 1,
name: 'Test',
usage: {
free: 25,
total: 30,
},
license_server_version: '',
license_key: '',
usage_api: '',
instances: [],
warnings: [],
},
]),
},
],
}),
],
};
Loading

0 comments on commit 34bb4f3

Please sign in to comment.