release v1.17

This commit is contained in:
shallegro
2024-12-03 17:38:18 +02:00
parent a7d2d320d2
commit 163ad8b8e9
593 changed files with 20227 additions and 12552 deletions

View File

@@ -1,53 +0,0 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"plugin:@ngrx/recommended-requiring-type-checking"
],
"rules": {
"no-console": "error",
"no-debugger": "error",
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "sm",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "sm",
"style": "kebab-case"
}
],
"@ngrx/prefer-effect-callback-in-block-statement": "off"
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended",
"plugin:@angular-eslint/template/accessibility"
],
"rules": {
"@angular-eslint/template/use-track-by-function": "warn"
}
}
]
}

52
eslint.config.js Normal file
View File

@@ -0,0 +1,52 @@
// @ts-check
const eslint = require("@eslint/js");
const tseslint = require("typescript-eslint");
const angular = require("angular-eslint");
module.exports = tseslint.config(
{
ignores: ["node_modules/*", "build/*", "dist/*", "electron/*"],
files: ["**/*.ts"],
extends: [
eslint.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
...angular.configs.tsRecommended,
],
processor: angular.processInlineTemplates,
rules: {
"no-console": "error",
"no-debugger": "error",
"quotes": ["error", "single", {
"allowTemplateLiterals": true
}],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "sm",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "sm",
"style": "kebab-case"
}
],
"@ngrx/prefer-effect-callback-in-block-statement": "off"
},
},
{
files: ["**/*.html"],
extends: [
...angular.configs.templateRecommended,
...angular.configs.templateAccessibility,
],
rules: {
"@angular-eslint/template/use-track-by-function": "warn"
},
}
);

12677
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +1,117 @@
{
"name": "clearml-webapp",
"version": "1.16.0",
"version": "1.17.0",
"license": "",
"scripts": {
"ng": "ng",
"start": "npx ng serve",
"start-widgets": "npx ng serve --port 4201 --project report-widgets --proxy-config proxy.config.mjs --live-reload false",
"hmr": "npx ng serve --live-reload true",
"build": "npx ng build --configuration production --source-map --vendor-chunk",
"build": "npx ng build --configuration production --source-map",
"build-dev": "node ./node_modules/.bin/ng build --extract-css=false",
"build-widgets": "npx ng build --project report-widgets --configuration production",
"fetch": "./scripts/get-remote-build.sh",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e --port 4300",
"bundle-report": "node --max_old_space_size=3248 ./node_modules/.bin/ng build --configuration production --vendor-chunk --aot --stats-json; webpack-bundle-analyzer build/stats.json",
"bundle-report": "node --max_old_space_size=3248 ./node_modules/.bin/ng build --configuration production --aot --stats-json; webpack-bundle-analyzer build/stats.json",
"submodule": "git submodule update --recursive --remote --init"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.7",
"@angular/cdk": "^17.3.7",
"@angular/common": "^17.3.7",
"@angular/compiler": "^17.3.7",
"@angular/core": "^17.3.7",
"@angular/forms": "^17.3.7",
"@angular/material": "^17.3.7",
"@angular/platform-browser": "^17.3.7",
"@angular/platform-browser-dynamic": "^17.3.7",
"@angular/platform-server": "^17.3.7",
"@angular/router": "^17.3.7",
"@angular/service-worker": "^17.3.7",
"@angular/youtube-player": "^17.3.7",
"@aws-sdk/client-s3": "^3.569.0",
"@aws-sdk/s3-request-presigner": "^3.569.0",
"@angular/animations": "^18.2.10",
"@angular/cdk": "^18.2.11",
"@angular/common": "^18.2.10",
"@angular/compiler": "^18.2.10",
"@angular/core": "^18.2.10",
"@angular/forms": "^18.2.10",
"@angular/material": "^18.2.11",
"@angular/platform-browser": "^18.2.10",
"@angular/platform-browser-dynamic": "^18.2.10",
"@angular/platform-server": "^18.2.10",
"@angular/router": "^18.2.10",
"@angular/service-worker": "^18.2.10",
"@angular/youtube-player": "^18.2.11",
"@aws-sdk/client-s3": "^3.637.0",
"@aws-sdk/s3-request-presigner": "^3.637.0",
"@ctrl/ngx-github-buttons": "^9.0.0",
"@ctrl/tinycolor": "^4.1.0",
"@ngneat/dag": "^2.0.0",
"@ngrx/effects": "^17.2.0",
"@ngrx/entity": "^17.2.0",
"@ngrx/router-store": "^17.2.0",
"@ngrx/store": "^17.2.0",
"ace-builds": "^1.33.1",
"@ngrx/component": "^18.1.1",
"@ngrx/effects": "^18.1.1",
"@ngrx/entity": "^18.1.1",
"@ngrx/operators": "^18.1.1",
"@ngrx/router-store": "^18.1.1",
"@ngrx/store": "^18.1.1",
"ace-builds": "^1.36.2",
"angular-resizable-element": "^7.0.2",
"angular-split": "^17.2.0",
"ansi-to-html": "^0.7.2",
"bootstrap": "^5.3.3",
"chart.js": "^4.4.2",
"chart.js": "^4.4.4",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-plugin-annotation": "^3.0.1",
"chartjs-plugin-zoom": "^2.0.1",
"curved-arrows": "^0.2.0",
"curved-arrows": "^0.3.0",
"d3-selection": "^3.0.0",
"date-fns": "^3.6.0",
"diff": "^5.2.0",
"dom-to-image": "^2.6.0",
"dompurify": "^3.1.3",
"dompurify": "^3.1.6",
"export-to-csv": "^1.3.0",
"filesize": "^10.1.1",
"has-ansi": "^5.0.1",
"filesize": "^10.1.4",
"has-ansi": "^6.0.0",
"hocon-parser": "^1.0.1",
"localforage": "^1.10.0",
"lodash-es": "^4.17.21",
"lucene": "^2.1.1",
"marked": "^12.0.2",
"ng2-charts": "^6.0.1",
"ngx-clipboard": "^16.0.0",
"ngx-color-picker": "^16.0.0",
"ngx-device-detector": "^7.0.0",
"ngx-color-picker": "^17.0.0",
"ngx-device-detector": "^8.0.0",
"ngx-markdown-editor": "^5.3.4",
"ngx-print": "^1.5.1",
"ngx-window-token": "^7.0.0",
"ngxtension": "^4.1.0",
"object-hash": "^3.0.0",
"primeicons": "^7.0.0",
"primeng": "^17.16.0",
"primeng": "^17.18.9",
"rxjs": "^7.8.1",
"string-to-color": "^2.2.2",
"taira": "^3.2.2",
"tslib": "^2.6.2",
"url": "^0.11.3",
"uuid": "^9.0.1",
"zone.js": "~0.14.5"
"tslib": "^2.7.0",
"url": "^0.11.4",
"uuid": "^10.0.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.6",
"@angular-devkit/core": "^17.3.6",
"@angular-devkit/schematics": "^17.3.6",
"@angular-devkit/schematics-cli": "^17.3.6",
"@angular-eslint/builder": "17.3.0",
"@angular-eslint/eslint-plugin": "17.3.0",
"@angular-eslint/eslint-plugin-template": "17.3.0",
"@angular-eslint/schematics": "17.3.0",
"@angular-eslint/template-parser": "17.3.0",
"@angular/cli": "^17.3.6",
"@angular/compiler-cli": "^17.3.7",
"@angular/language-service": "^17.3.7",
"@fortawesome/fontawesome-free": "^6.5.2",
"@ngrx/eslint-plugin": "^17.2.0",
"@ngrx/schematics": "^17.2.0",
"@ngrx/store-devtools": "^17.2.0",
"@angular-devkit/build-angular": "^18.2.11",
"@angular-devkit/core": "^18.2.11",
"@angular-devkit/schematics": "^18.2.11",
"@angular-devkit/schematics-cli": "^18.2.1",
"@angular/cli": "^18.2.11",
"@angular/compiler-cli": "^18.2.10",
"@angular/language-service": "^18.2.10",
"@fortawesome/fontawesome-free": "^6.6.0",
"@ngrx/eslint-plugin": "^18.1.1",
"@ngrx/schematics": "^18.1.1",
"@ngrx/store-devtools": "^18.1.1",
"@types/d3-selection": "^3.0.10",
"@types/dom-to-image": "^2.6.7",
"@types/dompurify": "^3.0.5",
"@types/has-ansi": "^5.0.2",
"@types/jasmine": "^5.1.4",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.10",
"@types/plotly.js": "^2.29.3",
"@types/node": "^20.14.10",
"@types/plotly.js": "^2.33.3",
"@types/tinycolor2": "^1.4.6",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsdoc": "^48.2.3",
"eslint-plugin-prefer-arrow": "^1.2.3",
"typescript": "~5.4.5"
"@types/uuid": "^10.0.0",
"angular-eslint": "^18.3.0",
"eslint": "^9.9.1",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "^50.2.2",
"eslint-plugin-prefer-arrow": "1.2.3",
"typescript": "^5.5.4",
"typescript-eslint": "^8.2.0"
}
}

View File

@@ -1,16 +1,13 @@
import {ApiUsersService} from './business-logic/api-services/users.service';
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
import {Component, OnDestroy, OnInit, ViewEncapsulation, HostListener, Renderer2, Injector} from '@angular/core';
import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
import {Component, OnDestroy, OnInit, ViewEncapsulation, HostListener, Renderer2, inject} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {Title} from '@angular/platform-browser';
import {selectBreadcrumbs, selectLoggedOut} from '@common/core/reducers/view.reducer';
import {Store} from '@ngrx/store';
import {selectRouterParams, selectRouterUrl} from '@common/core/reducers/router-reducer';
import {ApiProjectsService} from './business-logic/api-services/projects.service';
import {Project} from './business-logic/model/projects/project';
import {getAllSystemProjects, setSelectedProjectId, updateProject} from '@common/core/actions/projects.actions';
import {selectRouterProjectId, selectSelectedProject} from '@common/core/reducers/projects.reducer';
import {MatDialog} from '@angular/material/dialog';
import {getTutorialBucketCredentials} from '@common/core/actions/common-auth.actions';
import {termsOfUseAccepted} from '@common/core/actions/users.actions';
import {distinctUntilChanged, filter, tap} from 'rxjs/operators';
@@ -21,8 +18,6 @@ import {selectAvailableUpdates} from './core/reducers/view.reducer';
import {UPDATE_SERVER_PATH} from './app.constants';
import {aceReady, firstLogin, plotlyReady, setScaleFactor, visibilityChanged} from '@common/core/actions/layout.actions';
import {UiUpdatesService} from '@common/shared/services/ui-updates.service';
import {UsageStatsService} from './core/services/usage-stats.service';
import {dismissSurvey} from './core/actions/layout.actions';
import {getScaleFactor} from '@common/shared/utils/shared-utils';
import {ConfigurationService} from '@common/shared/services/configuration.service';
import {selectIsSharedAndNotOwner} from './features/experiments/reducers';
@@ -31,6 +26,7 @@ import {USER_PREFERENCES_KEY} from '@common/user-preferences';
import {Environment} from '../environments/base';
import {loadExternalLibrary} from '@common/shared/utils/load-external-library';
import {User} from '~/business-logic/model/users/user';
import {BreadcrumbsService} from '@common/shared/services/breadcrumbs.service';
@Component({
selector: 'sm-root',
@@ -39,24 +35,30 @@ import {User} from '~/business-logic/model/users/user';
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit, OnDestroy {
public loggedOut$: Observable<boolean>;
private router = inject(Router);
private titleService = inject(Title);
private store = inject(Store);
public serverUpdatesService = inject(ServerUpdatesService);
private uiUpdatesService = inject(UiUpdatesService);
private tipsService = inject(TipsService);
private renderer = inject(Renderer2);
private configService = inject(ConfigurationService);
private breadcrumbsService = inject(BreadcrumbsService); // don't delete
protected loggedOut$: Observable<boolean>;
private urlSubscription: Subscription;
public selectedProject$: Observable<Project>;
public projectId: string;
public isWorkersContext: boolean;
public updatesAvailable$: Observable<string>;
protected selectedProject$: Observable<Project>;
protected projectId: string;
protected isWorkersContext: boolean;
protected updatesAvailable$: Observable<string>;
private breadcrumbsSubscription: Subscription;
private selectedCurrentUserSubscription: Subscription;
public showNotification: boolean = true;
public demo = ConfigurationService.globalEnvironment.demo;
public isLoginContext: boolean;
public currentUser: User;
private gtmService;
public isSharedAndNotOwner$: Observable<boolean>;
protected showNotification = true;
protected isLoginContext: boolean;
protected currentUser: User;
protected isSharedAndNotOwner$: Observable<boolean>;
private activeWorkspace: string;
public hideUpdate: boolean;
public showSurvey: boolean;
private plotlyURL: string;
protected hideUpdate: boolean;
protected showSurvey: boolean;
private environment: Environment;
private title = 'ClearML';
@@ -70,22 +72,8 @@ export class AppComponent implements OnInit, OnDestroy {
}
constructor(
private router: Router,
private route: ActivatedRoute,
private titleService: Title,
private store: Store,
private projectsApi: ApiProjectsService,
private userService: ApiUsersService,
public serverUpdatesService: ServerUpdatesService,
private uiUpdatesService: UiUpdatesService,
private tipsService: TipsService,
private matDialog: MatDialog,
private userStats: UsageStatsService,
private renderer: Renderer2,
private injector: Injector,
private configService: ConfigurationService
) {
this.loggedOut$ = store.select(selectLoggedOut);
this.loggedOut$ = this.store.select(selectLoggedOut);
this.isSharedAndNotOwner$ = this.store.select(selectIsSharedAndNotOwner);
this.selectedProject$ = this.store.select(selectSelectedProject);
this.updatesAvailable$ = this.store.select(selectAvailableUpdates);
@@ -109,20 +97,11 @@ export class AppComponent implements OnInit, OnDestroy {
this.configService.globalEnvironmentObservable.subscribe(env => {
this.hideUpdate = env.hideUpdateNotice;
this.showSurvey = env.showSurvey;
this.plotlyURL = env.plotlyURL;
this.environment = env;
});
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe(
(item: NavigationEnd) => {
const gtmTag = {
event: 'page',
pageName: item.url
};
this.gtmService?.pushTag(gtmTag);
this.store.dispatch(routerActions.navigationEnd());
});
.subscribe(() => this.store.dispatch(routerActions.navigationEnd()));
this.selectedCurrentUserSubscription = this.store.select(selectCurrentUser).pipe(
tap(user => this.currentUser = user as unknown as User),
@@ -160,7 +139,9 @@ export class AppComponent implements OnInit, OnDestroy {
this.breadcrumbsSubscription = this.store.select(selectBreadcrumbs).subscribe(breadcrumbs => {
const crumbs = breadcrumbs.flat().filter((breadcrumb => !!breadcrumb?.name)).map(breadcrumb => breadcrumb.name);
crumbs.length> 0 && this.titleService.setTitle(`${this.title ? this.title + '-' : ''} ${crumbs.join(' / ')}`);
if (crumbs.length> 0) {
this.titleService.setTitle(`${this.title ? this.title + '-' : ''} ${crumbs.join(' / ')}`);
}
});
if (window.localStorage.getItem('disableHidpi') !== 'true') {
@@ -191,14 +172,6 @@ export class AppComponent implements OnInit, OnDestroy {
this.selectedCurrentUserSubscription.unsubscribe();
}
changeRoute(feature) {
return this.router.navigateByUrl('projects/' + this.projectId + '/' + feature);
}
backToProjects() {
return this.router.navigateByUrl('projects');
}
versionDismissed(version: string) {
this.serverUpdatesService.setDismissedVersion(version);
}
@@ -206,13 +179,4 @@ export class AppComponent implements OnInit, OnDestroy {
notifierActive(show: boolean) {
this.showNotification = show;
}
dismissSurvey() {
this.store.dispatch(dismissSurvey());
}
get guestUser(): boolean {
return !this.currentUser || this.currentUser?.role === 'guest';
}
}

View File

@@ -27,6 +27,7 @@ import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions} from '@angular/ma
import {UpdateNotifierComponent} from '@common/shared/ui-components/overlay/update-notifier/update-notifier.component';
import {ChooseColorModule} from '@common/shared/ui-components/directives/choose-color/choose-color.module';
import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spinner.component';
import {provideCharts, withDefaultRegisterables} from 'ng2-charts';
@NgModule({
declarations : [AppComponent],
@@ -62,7 +63,7 @@ import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spi
ChooseColorModule,
SpinnerComponent,
],
providers : [
providers: [
UserPreferences,
{provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {floatLabel: 'always'}},
{
@@ -77,7 +78,8 @@ import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spi
{
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
useValue: {position: 'above'} as MatTooltipDefaultOptions
}
},
provideCharts(withDefaultRegisterables()),
],
bootstrap : [AppComponent],
exports : []

View File

@@ -101,6 +101,11 @@ export const routes: Routes = [
loadChildren: () => import('./webapp-common/reports/reports.module').then(m => m.ReportsModule)
},
{path: 'workers-and-queues', loadChildren: () => import('./features/workers-and-queues/workers-and-queues.module').then(m => m.WorkersAndQueuesModule)},
{
path: 'endpoints',
loadChildren: () => import('./webapp-common/serving/serving.module').then(m => m.ServingModule),
canDeactivate: [resetContextMenuGuard]
},
{path: '404', loadChildren: () => import('./features/not-found/not-found.module').then(m => m.NotFoundModule)},
{path: '**', loadChildren: () => import('./features/not-found/not-found.module').then(m => m.NotFoundModule)},

View File

@@ -5,6 +5,7 @@ import {IApiRequest, SmHttpResponse} from '../model/api-request';
import {HttpClient, HttpParams} from '@angular/common/http';
import {HttpHeaders} from '@angular/common/http';
import {map} from 'rxjs/operators';
import {ApiOptions} from '~/business-logic/api-services/api';
@Injectable()
export class SmApiRequestsService {
@@ -35,7 +36,7 @@ export class SmApiRequestsService {
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}): Observable<T> {
}, extOptions?: ApiOptions): Observable<T> {
options.withCredentials = true;
return this.http.post<SmHttpResponse>(url, body, options).pipe(map(res => res.data));
}

View File

@@ -1,3 +1,7 @@
export * from './tasks.service';
import { TasksService } from './tasks.service';
export const APIS = [TasksService];
export interface ApiOptions {
headers?: Record<string, string>;
adminQuery?: boolean;
userId?: string;
}

View File

@@ -58,6 +58,9 @@ import { QueuesUpdateResponse } from '../model/queues/queuesUpdateResponse';
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import {QueuesClearQueueRequest} from '~/business-logic/model/queues/queuesClearQueueRequest';
import {ApiOptions} from '~/business-logic/api-services/api';
import {QueuesClearQueueResponse} from '~/business-logic/model/queues/queuesClearQueueResponse';
@Injectable()
@@ -93,7 +96,7 @@ export class ApiQueuesService {
/**
*
*
* Add or update queue metadata
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -138,7 +141,7 @@ export class ApiQueuesService {
}
/**
*
*
* Adds a task entry to the queue.
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -171,6 +174,7 @@ export class ApiQueuesService {
headers = headers.set("Content-Type", httpContentTypeSelected);
}
return this.apiRequest.post<QueuesAddTaskResponse>(`${this.basePath}/queues.add_task`,
request,
{
@@ -182,8 +186,23 @@ export class ApiQueuesService {
);
}
/**
*
/**
* Remove all tasks from the queue and change their statuses to what they were prior to enqueuing or \&#39;created\&#39;
* @param request request body
* @param options flags and headers to use in webapp
*/
public queuesClearQueue(request: QueuesClearQueueRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<QueuesClearQueueResponse>(`${this.basePath}/queues.clear_queue`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
);
}
/**
*
* Create a new queue
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -228,7 +247,7 @@ export class ApiQueuesService {
}
/**
*
*
* Deletes a queue. If the queue is not empty and force is not set to true, queue will not be deleted.
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -273,7 +292,7 @@ export class ApiQueuesService {
}
/**
*
*
* Delete metadata from queue
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -318,7 +337,7 @@ export class ApiQueuesService {
}
/**
*
*
* Get all queues
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -363,7 +382,7 @@ export class ApiQueuesService {
}
/**
*
*
* Get all queues
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -408,7 +427,7 @@ export class ApiQueuesService {
}
/**
*
*
* Gets queue information
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -453,8 +472,8 @@ export class ApiQueuesService {
}
/**
*
*
*
*
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
@@ -498,7 +517,7 @@ export class ApiQueuesService {
}
/**
*
*
* Gets the next task from the top of the queue (FIFO). The task entry is removed from the queue.
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -543,7 +562,7 @@ export class ApiQueuesService {
}
/**
*
*
* Returns metrics of the company queues. The metrics are avaraged in the specified interval.
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -588,8 +607,8 @@ export class ApiQueuesService {
}
/**
*
*
*
*
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
@@ -633,7 +652,7 @@ export class ApiQueuesService {
}
/**
*
*
* Moves a task entry one step forward towards the top of the queue.
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -678,8 +697,8 @@ export class ApiQueuesService {
}
/**
*
*
*
*
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
@@ -723,8 +742,8 @@ export class ApiQueuesService {
}
/**
*
*
*
*
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
@@ -768,7 +787,7 @@ export class ApiQueuesService {
}
/**
*
*
* Removes a task entry from the queue.
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
@@ -813,7 +832,7 @@ export class ApiQueuesService {
}
/**
*
*
* Update queue information
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.

View File

@@ -0,0 +1,171 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/member-ordering */
import {HTTP} from '~/app.constants';
import {SmApiRequestsService} from './api-requests.service';
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent } from '@angular/common/http';
import { CustomHttpUrlEncodingCodec } from '../encoder';
import { ApiOptions } from './api';
import { Observable } from 'rxjs';
import { ServingContainerStatusReportRequest } from '../model/serving/servingContainerStatusReportRequest';
import { ServingGetEndpointDetailsRequest } from '../model/serving/servingGetEndpointDetailsRequest';
import { ServingGetEndpointDetailsResponse } from '../model/serving/servingGetEndpointDetailsResponse';
import { ServingGetEndpointsResponse } from '../model/serving/servingGetEndpointsResponse';
import { ServingRegisterContainerRequest } from '../model/serving/servingRegisterContainerRequest';
import { ServingUnregisterContainerRequest } from '../model/serving/servingUnregisterContainerRequest';
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import {ServingGetLoadingInstancesResponse} from '~/business-logic/model/serving/servingGetLoadingInstancesResponse';
import {ServingGetEndpointMetricsHistoryRequest} from '~/business-logic/model/serving/servingGetEndpointMetricsHistoryRequest';
import {ServingGetEndpointMetricsHistoryResponse} from '~/business-logic/model/serving/servingGetEndpointMetricsHistoryResponse';
@Injectable()
export class ApiServingService {
protected basePath = HTTP.API_BASE_URL;
public defaultHeaders = new HttpHeaders({'Accept': 'application/json'});
public configuration = new Configuration();
constructor(protected apiRequest: SmApiRequestsService, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) {
if (basePath) {
this.basePath = basePath;
}
if (configuration) {
this.configuration = configuration;
this.basePath = basePath || configuration.basePath || this.basePath;
}
}
/**
* Container status report
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingContainerStatusReport(request: ServingContainerStatusReportRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<object>(`${this.basePath}/serving.container_status_report`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Get endpoint details
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingGetEndpointDetails(request: ServingGetEndpointDetailsRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<ServingGetEndpointDetailsResponse>(`${this.basePath}/serving.get_endpoint_details`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Get endpoint charts
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingGetEndpointMetricsHistory(request: ServingGetEndpointMetricsHistoryRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<ServingGetEndpointMetricsHistoryResponse>(`${this.basePath}/serving.get_endpoint_metrics_history`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Get all the registered endpoints
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingGetEndpoints(request: object, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<ServingGetEndpointsResponse>(`${this.basePath}/serving.get_endpoints`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Get loading instances (enpoint_url not set yet)
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingGetLoadingInstances(request: object, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<ServingGetLoadingInstancesResponse>(`${this.basePath}/serving.get_loading_instances`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Register container
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingRegisterContainer(request: ServingRegisterContainerRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<object>(`${this.basePath}/serving.register_container`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Unregister container
* @param request request body
* @param options flags and headers to use in webapp
*/
public servingUnregisterContainer(request: ServingUnregisterContainerRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<object>(`${this.basePath}/serving.unregister_container`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
}

View File

@@ -117,6 +117,9 @@ import { BASE_PATH, COLLECTION_FORMATS } from '../variables'
import { Configuration } from '../configuration';
import {TasksUpdateTagsResponse} from '~/business-logic/model/tasks/tasksUpdateTagsResponse';
import {TasksUpdateTagsRequest} from '~/business-logic/model/tasks/tasksUpdateTagsRequest';
import {TasksGetOperationsLogRequest} from '~/business-logic/model/tasks/tasksGetOperationsLogRequest';
import {TasksGetOperationsLogResponse} from '~/business-logic/model/tasks/tasksGetOperationsLogResponse';
import {ApiOptions} from '~/business-logic/api-services/api';
@Injectable()
@@ -1366,7 +1369,22 @@ export class ApiTasksService {
);
}
/**
/**
* Get the task operations log
* @param request request body
* @param options flags and headers to use in webapp
*/
public tasksGetOperationsLog(request: TasksGetOperationsLogRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<TasksGetOperationsLogResponse>(`${this.basePath}/tasks.get_operations_log`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
);
}
/**
*
* Get the list of task configurations
* @param request request body

View File

@@ -17,6 +17,7 @@ import {ApiOrganizationService} from './api-services/organization.service';
import {ApiLoginService} from './api-services/login.service';
import {ApiPipelinesService} from '~/business-logic/api-services/pipelines.service';
import {ApiReportsService} from '~/business-logic/api-services/reports.service';
import {ApiServingService} from '~/business-logic/api-services/serving.service';
@NgModule({
imports : [CommonModule, HttpClientModule],
@@ -39,6 +40,7 @@ import {ApiReportsService} from '~/business-logic/api-services/reports.service';
ApiLoginService,
ApiPipelinesService,
ApiReportsService,
ApiServingService
]
})
export class BusinessLogicModule {

View File

@@ -0,0 +1,20 @@
/**
* queues
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface QueuesClearQueueRequest {
/**
* Queue id
*/
queue: string;
}

View File

@@ -0,0 +1,20 @@
/**
* queues
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface QueuesClearQueueResponse {
/**
* IDs of the removed tasks
*/
removed_tasks?: Array<string>;
}

View File

@@ -21,4 +21,8 @@ export interface QueuesRemoveTaskRequest {
* Task id
*/
task: string;
/**
* If set to \'true\' then change the removed task status to the one it had prior to enqueuing or \'created\'
*/
update_task_status?: boolean;
}

View File

@@ -0,0 +1,60 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ContainerInfo {
/**
* Endpoint name
*/
endpoint?: string;
/**
* Model name
*/
model?: string;
/**
* Model url
*/
url?: string;
/**
* Model source
*/
model_source?: string;
/**
* Model version
*/
model_version?: string;
/**
* Preprocess Artifact
*/
preprocess_artifact?: string;
/**
* Input type
*/
input_type?: string;
/**
* Input size in bytes
*/
input_size?: number;
/**
* Container ID
*/
id: string;
/**
* Model instance uptime in seconds
*/
uptime_sec?: number;
/**
* The latest time when the container instance sent update
*/
last_update?: string;
}

View File

@@ -0,0 +1,45 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.31
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { ContainerInstanceStatsReference } from '././containerInstanceStatsReference';
export interface ContainerInstanceStats {
/**
* Container ID
*/
id?: string;
/**
* Uptime in seconds
*/
uptime_sec?: number;
/**
* Number of requests
*/
requests?: number;
/**
* Average requests per minute
*/
requests_min?: number;
/**
* Average request latency in ms
*/
latency_ms?: number;
/**
* The latest time when the container instance sent update
*/
last_update?: string;
/**
* Array of reference items provided by the container instance. Can contain multiple reference items with the same type
*/
reference?: Array<ContainerInstanceStatsReference>;
}

View File

@@ -0,0 +1,34 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.31
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ContainerInstanceStatsReference {
/**
* The type of the reference item
*/
type: ContainerInstanceStatsReference.TypeEnum;
/**
* The reference item value
*/
value: string;
}
export namespace ContainerInstanceStatsReference {
export type TypeEnum = 'app_id' | 'app_instance' | 'model' | 'task' | 'url';
export const TypeEnum = {
AppId: 'app_id' as TypeEnum,
AppInstance: 'app_instance' as TypeEnum,
Model: 'model' as TypeEnum,
Task: 'task' as TypeEnum,
Url: 'url' as TypeEnum
}
}

View File

@@ -0,0 +1,53 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface EndpointStats {
/**
* Endpoint name
*/
id: string;
endpoint?: string;
/**
* Model name
*/
model?: string;
/**
* Model url
*/
url?: string;
/**
* The number of model serving instances
*/
instances?: number;
/**
* Max of model instance uptime in seconds
*/
uptime_sec?: number;
/**
* Total requests processed by model instances
*/
requests?: number;
/**
* Average of request rate of model instances per minute
*/
requests_min?: number;
/**
* Average of latency of model instances in ms
*/
latency_ms?: number;
/**
* The latest time when one of the model instances was updated
*/
last_update?: string;
}

View File

@@ -0,0 +1,76 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface MachineStats {
/**
* Average CPU usage per core
*/
cpu_usage?: Array<number>;
/**
* Average GPU usage per GPU card
*/
gpu_usage?: Array<number>;
/**
* Used memory MBs
*/
memory_used?: number;
/**
* Free memory MBs
*/
memory_free?: number;
/**
* GPU free memory MBs
*/
gpu_memory_free?: Array<number>;
/**
* GPU used memory MBs
*/
gpu_memory_used?: Array<number>;
/**
* Mbytes per second
*/
network_tx?: number;
/**
* Mbytes per second
*/
network_rx?: number;
/**
* Free space in % of /home drive
*/
disk_free_home?: number;
/**
* Free space in % of /tmp drive
*/
disk_free_temp?: number;
/**
* Mbytes read per second
*/
disk_read?: number;
/**
* Mbytes write per second
*/
disk_write?: number;
/**
* CPU temperature
*/
cpu_temperature?: Array<number>;
/**
* GPU temperature
*/
gpu_temperature?: Array<number>;
/**
* GPU fraction
*/
gpu_fraction?: Array<number>;
}

View File

@@ -0,0 +1,15 @@
export * from '././containerInfo';
export * from '././containerInstanceStats';
export * from '././endpointStats';
export * from '././machineStats';
export * from '././servingContainerStatusReportRequest';
export * from '././servingGetEndpointDetailsRequest';
export * from '././servingGetEndpointDetailsResponse';
export * from '././servingGetEndpointMetricsHistoryRequest';
export * from '././servingGetEndpointMetricsHistoryResponse';
export * from '././servingGetEndpointMetricsHistoryResponseInstances';
export * from '././servingGetEndpointMetricsHistoryResponseTotal';
export * from '././servingGetEndpointsResponse';
export * from '././servingGetLoadingInstancesResponse';
export * from '././servingRegisterContainerRequest';
export * from '././servingUnregisterContainerRequest';

View File

@@ -0,0 +1,70 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { MachineStats } from '././machineStats';
export interface ServingContainerStatusReportRequest {
/**
* Container ID
*/
container_id: string;
/**
* Endpoint name
*/
endpoint_name: string;
/**
* Endpoint URL
*/
endpoint_url?: string;
/**
* Model name
*/
model_name: string;
/**
* Model source
*/
model_source?: string;
/**
* Model version
*/
model_version?: string;
/**
* Preprocess Artifact
*/
preprocess_artifact?: string;
/**
* Input type
*/
input_type?: string;
/**
* Input size in bytes
*/
input_size?: number;
/**
* Uptime in seconds
*/
uptime_sec?: number;
/**
* Number of requests
*/
requests_num?: number;
/**
* Average requests per minute
*/
requests_min?: number;
/**
* Average request latency in ms
*/
latency_ms?: number;
machine_stats?: MachineStats;
}

View File

@@ -0,0 +1,20 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingGetEndpointDetailsRequest {
/**
* Endpoint URL
*/
endpoint_url: string;
}

View File

@@ -0,0 +1,58 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { ContainerInstanceStats } from '././containerInstanceStats';
export interface ServingGetEndpointDetailsResponse {
/**
* Endpoint name
*/
endpoint?: string;
/**
* Model name
*/
model?: string;
/**
* Model url
*/
url?: string;
/**
* Model source
*/
model_source?: string;
/**
* Model version
*/
model_version?: string;
/**
* Preprocess Artifact
*/
preprocess_artifact?: string;
/**
* Input type
*/
input_type?: string;
/**
* Input size in bytes
*/
input_size?: number;
/**
* Max of model instance uptime in seconds
*/
uptime_sec?: number;
/**
* The latest time when one of the model instances was updated
*/
last_update?: Date;
instances?: Array<ContainerInstanceStats>;
}

View File

@@ -0,0 +1,58 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingGetEndpointMetricsHistoryRequest {
/**
* Endpoint Url
*/
endpoint_url: string;
/**
* Starting time (in seconds from epoch) for collecting statistics
*/
from_date: number;
/**
* Ending time (in seconds from epoch) for collecting statistics
*/
to_date: number;
/**
* Time interval in seconds for a single statistics point. The minimal value is 1
*/
interval: number;
/**
* The type of the metrics to return on the chart
*/
metric_type?: ServingGetEndpointMetricsHistoryRequest.MetricTypeEnum;
/**
* If set then return instance charts and total. Otherwise total only
*/
instance_charts?: boolean;
}
export namespace ServingGetEndpointMetricsHistoryRequest {
export type MetricTypeEnum = 'requests' | 'requests_min' | 'latency_ms' | 'cpu_count' | 'gpu_count' | 'cpu_util' | 'gpu_util' | 'ram_total' | 'ram_free' | 'gpu_ram_total' | 'gpu_ram_free' | 'network_rx' | 'network_tx';
export const MetricTypeEnum = {
Requests: 'requests' as MetricTypeEnum,
RequestsMin: 'requests_min' as MetricTypeEnum,
LatencyMs: 'latency_ms' as MetricTypeEnum,
CpuCount: 'cpu_count' as MetricTypeEnum,
GpuCount: 'gpu_count' as MetricTypeEnum,
CpuUtil: 'cpu_util' as MetricTypeEnum,
GpuUtil: 'gpu_util' as MetricTypeEnum,
RamTotal: 'ram_total' as MetricTypeEnum,
RamFree: 'ram_free' as MetricTypeEnum,
GpuRamTotal: 'gpu_ram_total' as MetricTypeEnum,
GpuRamFree: 'gpu_ram_free' as MetricTypeEnum,
NetworkRx: 'network_rx' as MetricTypeEnum,
NetworkTx: 'network_tx' as MetricTypeEnum
}
}

View File

@@ -0,0 +1,27 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { ServingGetEndpointMetricsHistoryResponseTotal } from '././servingGetEndpointMetricsHistoryResponseTotal';
import { ServingGetEndpointMetricsHistoryResponseInstances } from '././servingGetEndpointMetricsHistoryResponseInstances';
export interface ServingGetEndpointMetricsHistoryResponse {
/**
* The inteval that was actually used for the histogram. May be larger then the requested one
*/
computed_interval?: number;
total?: ServingGetEndpointMetricsHistoryResponseTotal;
/**
* Instance charts
*/
instances?: { [key: string]: ServingGetEndpointMetricsHistoryResponseInstances; };
}

View File

@@ -0,0 +1,28 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingGetEndpointMetricsHistoryResponseInstances {
/**
* The title of the series
*/
title?: string;
/**
* List of timestamps (in seconds from epoch) in the acceding order. The timestamps are separated by the requested interval.
*/
dates?: Array<number>;
/**
* List of values corresponding to the timestamps in the dates list.
*/
values?: Array<number>;
}

View File

@@ -0,0 +1,28 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.31
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingGetEndpointMetricsHistoryResponseInstancesValue {
/**
* The title of the series
*/
title?: string;
/**
* List of timestamps (in seconds from epoch) in the acceding order. The timestamps are separated by the requested interval.
*/
dates?: Array<number>;
/**
* List of values corresponding to the timestamps in the dates list.
*/
values?: Array<number>;
}

View File

@@ -0,0 +1,29 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingGetEndpointMetricsHistoryResponseTotal {
/**
* The title of the series
*/
title?: string;
/**
* List of timestamps (in seconds from epoch) in the acceding order. The timestamps are separated by the requested interval.
*/
dates?: Array<number>;
/**
* List of values corresponding to the timestamps in the dates list.
*/
values?: Array<number>;
description?: object;
}

View File

@@ -0,0 +1,18 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { EndpointStats } from '././endpointStats';
export interface ServingGetEndpointsResponse {
endpoints?: Array<EndpointStats>;
}

View File

@@ -0,0 +1,18 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { ContainerInfo } from '././containerInfo';
export interface ServingGetLoadingInstancesResponse {
instances?: Array<ContainerInfo>;
}

View File

@@ -0,0 +1,56 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingRegisterContainerRequest {
/**
* Container ID
*/
container_id: string;
/**
* Endpoint name
*/
endpoint_name: string;
/**
* Endpoint URL
*/
endpoint_url?: string;
/**
* Model name
*/
model_name: string;
/**
* Model source
*/
model_source?: string;
/**
* Model version
*/
model_version?: string;
/**
* Preprocess Artifact
*/
preprocess_artifact?: string;
/**
* Input type
*/
input_type?: string;
/**
* Input size in bytes
*/
input_size?: number;
/**
* Registration timeout in seconds. If timeout seconds have passed since the service container last call to register or status_report, the container is automatically removed from the list of registered containers.
*/
timeout?: number;
}

View File

@@ -0,0 +1,20 @@
/**
* serving
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ServingUnregisterContainerRequest {
/**
* Container ID
*/
container_id: string;
}

View File

@@ -116,7 +116,7 @@ export interface Task {
/**
* Last time any update was done to the task
*/
last_change?: string;
last_change?: Date;
/**
* Last iteration reported for this task
*/

View File

@@ -0,0 +1,39 @@
/**
* tasks
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface TasksGetOperationsLogRequest {
/**
* Task ID
*/
task: string;
/**
* The amount of records to retrieve
*/
page_size?: number;
/**
* The page to retrieve
*/
page?: number;
/**
* The time sorting
*/
order?: TasksGetOperationsLogRequest.OrderEnum;
}
export namespace TasksGetOperationsLogRequest {
export type OrderEnum = 'asc' | 'desc';
export const OrderEnum = {
Asc: 'asc' as OrderEnum,
Desc: 'desc' as OrderEnum
}
}

View File

@@ -0,0 +1,18 @@
/**
* tasks
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { TasksGetOperationsLogResponseOperations } from '././tasksGetOperationsLogResponseOperations';
export interface TasksGetOperationsLogResponse {
operations?: Array<TasksGetOperationsLogResponseOperations>;
}

View File

@@ -0,0 +1,70 @@
/**
* tasks
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { TasksGetOperationsLogResponseUser } from '././tasksGetOperationsLogResponseUser';
export interface TasksGetOperationsLogResponseOperations {
/**
* Log record ID
*/
id?: string;
/**
* Log record creation time
*/
created?: string;
/**
* The operation that was performed on the task. Either new task status or \'deleted\'
*/
operation?: string;
/**
* The reason for the task operation
*/
reason?: string;
/**
* The info that was passed by the client for the task operation if any
*/
info?: string;
/**
* The calling client IP
*/
ip?: string;
/**
* The calling agent
*/
originator?: TasksGetOperationsLogResponseOperations.OriginatorEnum;
/**
* The calling agent version
*/
originator_version?: string;
/**
* The calling client id
*/
originator_id?: string;
user?: TasksGetOperationsLogResponseUser;
}
export namespace TasksGetOperationsLogResponseOperations {
export type OriginatorEnum = 'agent' | 'webapp' | 'enterprise SDK' | 'SDK' | 'session' | 'serving' | 'task' | 'python' | 'curl' | 'postman' | 'other';
export const OriginatorEnum = {
Agent: 'agent' as OriginatorEnum,
Webapp: 'webapp' as OriginatorEnum,
EnterpriseSDK: 'enterprise SDK' as OriginatorEnum,
SDK: 'SDK' as OriginatorEnum,
Session: 'session' as OriginatorEnum,
Serving: 'serving' as OriginatorEnum,
Task: 'task' as OriginatorEnum,
Python: 'python' as OriginatorEnum,
Curl: 'curl' as OriginatorEnum,
Postman: 'postman' as OriginatorEnum,
Other: 'other' as OriginatorEnum
}
}

View File

@@ -0,0 +1,24 @@
/**
* tasks
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface TasksGetOperationsLogResponseUser {
/**
* User ID
*/
id?: string;
/**
* User name
*/
name?: string;
}

View File

@@ -28,5 +28,6 @@ export interface GetCurrentUserResponseUserObject {
/**
* User preferences
*/
preferences?: object;
preferences?: any;
created_in_version?: string;
}

View File

@@ -0,0 +1,28 @@
/**
* workers
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ResourceUsageSeries {
/**
* The title of the series
*/
title?: string;
/**
* List of timestamps (in seconds from epoch) in the acceding order. The timestamps are separated by the requested interval.
*/
dates?: Array<number>;
/**
* List of values corresponding to the timestamps in the dates list.
*/
values?: Array<number>;
}

View File

@@ -1,15 +1,12 @@
import {Injectable} from '@angular/core';
import {TASK_TYPES} from '../../app.constants';
import {TAGS, TASKS_STATUS} from '../../webapp-common/tasks/tasks.constants';
import {TASK_TYPES} from '~/app.constants';
import {TAGS, TASKS_STATUS} from '@common/tasks/tasks.constants';
import {Task} from '../model/tasks/task';
import {DEFAULT_QUEUE_TAG} from '../variables';
import {ISelectedExperiment} from '~/features/experiments/shared/experiment-info.model';
@Injectable()
export class BlTasksService {
constructor() {
}
private previouslyUsedQueue;
getDefaultQueue(queues) {
@@ -24,11 +21,11 @@ export class BlTasksService {
return queue.system_tags && queue.system_tags.includes(DEFAULT_QUEUE_TAG);
}
canEnqueue(task: Task): boolean {
canEnqueue(task: ISelectedExperiment): boolean {
return !!(task && (TASKS_STATUS.CREATED === task.status || TASKS_STATUS.STOPPED === task.status) && task.type !== TASK_TYPES.MANUAL_ANNOTATION);
}
canDequeue(task: Task): boolean {
canDequeue(task: ISelectedExperiment): boolean {
return !!(task && TASKS_STATUS.QUEUED === task.status);
}

View File

@@ -12,10 +12,6 @@ import {messagesReducer} from '@common/core/reducers/messages-reducer';
import {projectsReducer} from '@common/core/reducers/projects.reducer';
import {routerReducer} from '@common/core/reducers/router-reducer';
import {SmSyncStateSelectorService} from '@common/core/services/sync-state-selector.service';
import {
EXPERIMENTS_COMPARE_METRICS_CHARTS_
} from '@common/experiments-compare/actions/experiments-compare-charts.actions';
import {compareSyncedKeys} from '@common/experiments-compare/experiments-compare.module';
import {PROJECTS_PREFIX} from '@common/projects/common-projects.consts';
import {CHOOSE_COLOR_PREFIX} from '@common/shared/ui-components/directives/choose-color/choose-color.actions';
import {colorSyncedKeys} from '@common/shared/ui-components/directives/choose-color/choose-color.module';
@@ -27,10 +23,10 @@ import {USERS_PREFIX, VIEW_PREFIX} from '~/app.constants';
import {ProjectsEffects} from '~/core/effects/projects.effects';
import {loginReducer} from '~/features/login/login.reducer';
import {projectSyncedKeys} from '~/features/projects/projects.module';
import {authReducer} from '~/features/settings/containers/admin/auth.reducers';
import {authReducer} from '~/core/reducers/auth.reducers';
import {AdminService} from '~/shared/services/admin.service';
import {UserEffects} from './effects/users.effects';
import {usageStatsReducer} from './reducers/usage-stats.reducer';
import {usageStatsReducer, userStatsFeatureKey} from './reducers/usage-stats.reducer';
import {usersReducer} from './reducers/users.reducer';
import {viewReducer} from './reducers/view.reducer';
import {UsageStatsService} from './services/usage-stats.service';
@@ -49,7 +45,7 @@ export const reducers = {
users: usersReducer,
login: loginReducer,
rootProjects: projectsReducer,
userStats: usageStatsReducer
[userStatsFeatureKey]: usageStatsReducer
};
const syncedKeys = [

View File

@@ -1,5 +1,6 @@
import {Injectable} from '@angular/core';
import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';
import {Actions, createEffect, ofType} from '@ngrx/effects';
import {concatLatestFrom} from '@ngrx/operators';
import * as actions from '../../webapp-common/core/actions/projects.actions';
import {Store} from '@ngrx/store';
import {selectSelectedProjectId, selectShowHidden} from '@common/core/reducers/projects.reducer';
@@ -10,7 +11,7 @@ import {requestFailed} from '@common/core/actions/http.actions';
import {ApiProjectsService} from '~/business-logic/api-services/projects.service';
import {selectCurrentUser, selectShowOnlyUserWork,} from '@common/core/reducers/users-reducer';
import {ProjectsGetAllExRequest} from '~/business-logic/model/projects/projectsGetAllExRequest';
import {selectRouterConfig} from "@common/core/reducers/router-reducer";
import {selectRouterConfig} from '@common/core/reducers/router-reducer';
@Injectable()

View File

@@ -1,6 +1,6 @@
import {Injectable} from '@angular/core';
import {Actions, ofType, createEffect} from '@ngrx/effects';
import {filter, take, mergeMap, switchMap, catchError} from 'rxjs/operators';
import {mergeMap, switchMap, catchError, map} from 'rxjs/operators';
import {ApiServerService} from '~/business-logic/api-services/server.service';
import {ServerReportStatsOptionResponse} from '~/business-logic/model/server/serverReportStatsOptionResponse';
import {setUsageStats, updateUsageStats} from '../actions/usage-stats.actions';
@@ -16,20 +16,18 @@ export class UserEffects {
constructor(private actions: Actions, private userService: ApiUsersService, private serverService: ApiServerService) {}
setUser$ = createEffect(() => this.actions.pipe(
ofType(fetchCurrentUser),
filter(user => !!user),
take(1),
mergeMap(() => this.serverService.serverReportStatsOption({})
.pipe(
switchMap((options: ServerReportStatsOptionResponse) => [setUsageStats({
allowed: options.enabled,
currVersion: options.current_version,
allowedVersion: options.enabled_version
})])
)
)
));
setUser$ = createEffect(() => {
return this.actions.pipe(
ofType(fetchCurrentUser),
mergeMap(() => this.serverService.serverReportStatsOption({})),
switchMap((options: ServerReportStatsOptionResponse) => [setUsageStats({
supported: options.supported,
allowed: options.enabled,
currVersion: options.current_version,
allowedVersion: options.enabled_version
})])
);
});
setStatsPref$ = createEffect(
() => this.actions.pipe(
@@ -38,6 +36,7 @@ export class UserEffects {
(action) => this.serverService.serverReportStatsOption({enabled: action.allowed})
.pipe(
switchMap((options: ServerReportStatsOptionResponse) => [setUsageStats({
supported: options.supported,
allowed: options.enabled,
currVersion: options.current_version,
allowedVersion: options.enabled_version
@@ -52,10 +51,10 @@ export class UserEffects {
// eslint-disable-next-line @typescript-eslint/naming-convention
mergeMap(() => this.userService.usersGetCurrentUser({get_supported_features: true})
.pipe(
switchMap((res: UsersGetCurrentUserResponse) => [setCurrentUser({
map((res: UsersGetCurrentUserResponse) => setCurrentUser({
user: res.user,
gettingStarted: res.getting_started as unknown as GettingStarted, settings: res.settings
})]),
})),
catchError(error => [requestFailed(error)])
)
)

View File

@@ -0,0 +1,12 @@
import {commonAuthReducer, initAuth, selectAuth} from '@common/core/reducers/common-auth-reducer';
import {createReducer, createSelector} from '@ngrx/store';
const extraCredentials = [];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const selectExtraCredentials = createSelector(selectAuth, state => extraCredentials);
export const authReducer = createReducer(
initAuth,
...commonAuthReducer
);

View File

@@ -1,10 +1,11 @@
import {Action, createReducer, on, createSelector} from '@ngrx/store';
import {createReducer, on, createSelector} from '@ngrx/store';
import {setUsageStats} from '../actions/usage-stats.actions';
export const userStatsFeatureKey = 'userStats';
export interface UsageStatState {
supported: boolean;
allowed: boolean;
currVersion: string;
allowedVersion: string;
@@ -12,19 +13,20 @@ export interface UsageStatState {
export const initialState: UsageStatState = {
supported: null,
allowed: null,
currVersion: '',
allowedVersion: ''
};
const _statsReducer = createReducer(initialState,
export const usageStatsReducer = createReducer(
initialState,
on(setUsageStats, (state: UsageStatState, newState) => ({...state, ...newState}))
);
export const usageStatsReducer = (state = initialState, action: Action) => _statsReducer(state, action);
export const selectSendStats = state => state.userStatsFeatureKey;
export const selectSendStats = state => state[userStatsFeatureKey] as UsageStatState;
export const selectStatsSupported = createSelector(selectSendStats, (state) => state.supported);
export const selectAllowed = createSelector(selectSendStats, (state) => state?.allowed);
export const selectCurrentVersion = createSelector(selectSendStats, (state) => state?.currVersion);
export const selectAllowedVersion = createSelector(selectSendStats, (state) => state?.allowedVersion);

View File

@@ -1,9 +1,9 @@
import {Component, inject, OnDestroy, OnInit} from '@angular/core';
import {Subscription} from 'rxjs';
import {Subscription, combineLatest} from 'rxjs';
import {Store} from '@ngrx/store';
import {ActivatedRoute, Router} from '@angular/router';
import {selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
import {filter, skip, take} from 'rxjs/operators';
import {selectCurrentUser, selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
import {debounceTime, filter, take} from 'rxjs/operators';
import {setDeep} from '@common/core/actions/projects.actions';
import {getRecentProjects, getRecentExperiments} from '@common/dashboard/common-dashboard.actions';
import {selectFirstLogin} from '@common/core/reducers/view.reducer';
@@ -29,7 +29,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
public recentTasks$ = this.store.select(selectRecentTasks);
public width: number;
private welcomeSub: Subscription;
private subscriptions = new Subscription();
constructor() {
this.store.dispatch(initSearch({payload: 'Search for all'}));
@@ -41,12 +40,19 @@ export class DashboardComponent implements OnInit, OnDestroy {
)
.subscribe(() => this.router.navigate(['search'], {relativeTo: this.activatedRoute, queryParamsHandling: 'preserve'}));
this.subscriptions.add(this.store.select(selectShowOnlyUserWork).pipe(
skip(1),
).subscribe(() => {
this.store.dispatch(getRecentProjects());
this.store.dispatch(getRecentExperiments());
}));
combineLatest([
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser)
])
.pipe(
debounceTime(100),
takeUntilDestroyed(),
filter(([, user]) => !!user),
)
.subscribe(() => {
this.store.dispatch(getRecentProjects());
this.store.dispatch(getRecentExperiments());
});
this.welcomeSub = this.store.select(selectFirstLogin)
.pipe(

View File

@@ -9,6 +9,8 @@ import {ProjectsSharedModule} from '~/features/projects/shared/projects-shared.m
import {LabeledFormFieldDirective} from '@common/shared/directive/labeled-form-field.directive';
import {ButtonToggleComponent} from '@common/shared/ui-components/inputs/button-toggle/button-toggle.component';
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {PushPipe} from '@ngrx/component';
import {DotsLoadMoreComponent} from '@common/shared/ui-components/indicators/dots-load-more/dots-load-more.component';
@NgModule({
@@ -21,7 +23,9 @@ import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indic
ProjectsSharedModule,
LabeledFormFieldDirective,
ButtonToggleComponent,
ShowTooltipIfEllipsisDirective
ShowTooltipIfEllipsisDirective,
PushPipe,
DotsLoadMoreComponent
],
declarations: [
SimpleDatasetsComponent,

View File

@@ -4,7 +4,7 @@
border-bottom: 1px solid #efefef;
&.minimized {
grid-template-columns: 0 1fr 60px;
grid-template-columns: 0 1fr 90px;
}
@media(max-width: 1200px) {
@@ -14,4 +14,4 @@
sm-router-tab-nav-bar {
overflow: hidden;
}
}

View File

@@ -1,4 +1,4 @@
import {Component, Input} from '@angular/core';
import {Component, computed} from '@angular/core';
import {ExperimentMenuComponent} from '@common/experiments/shared/components/experiment-menu/experiment-menu.component';
@Component({
@@ -7,8 +7,5 @@ import {ExperimentMenuComponent} from '@common/experiments/shared/components/exp
styleUrls: ['../../../../webapp-common/experiments/shared/components/experiment-menu/experiment-menu.component.scss']
})
export class ExperimentMenuExtendedComponent extends ExperimentMenuComponent{
set contextMenu(data) {}
get contextMenu() {
return this;
}
contextMenu = computed(() => this as ExperimentMenuComponent);
}

View File

@@ -5,32 +5,49 @@
<div class="experiment-output-container light-theme" [class.minimized]="minimized">
<sm-experiment-info-header
[experiment]="selectedExperiment"
[infoData]="infoData$ | async"
[infoData]="infoData$ | ngrxPush"
[editable]="!isExample"
[showMenu]="true"
[minimized]="minimized"
[isSharedAndNotOwner]="isSharedAndNotOwner$ | async"
[isSharedAndNotOwner]="isSharedAndNotOwner$ | ngrxPush"
(experimentNameChanged)="updateExperimentName($event)"
(closeInfoClicked)="closePanel()"
(minimizeClicked)="minimizeView()"
(maximizedClicked)="maximize()"
>
</sm-experiment-info-header>
<sm-experiment-info-navbar [minimized]="minimized" [splitSize]="selectSplitSize$ | async">
<span class="refresh-position" refresh>
<sm-experiment-settings
[class.maximized]="!minimized"
[showSettings]="routerConfig.includes('scalar') && minimized"
(toggleSettings)="toggleSettingsBar()">
</sm-experiment-settings>
<sm-refresh-button
*ngIf=" ! minimized"
class="light-theme"
(setAutoRefresh)="setAutoRefresh($event)"
>
</sm-refresh-button>
></sm-experiment-info-header>
<sm-experiment-info-navbar [minimized]="minimized" [splitSize]="selectSplitSize$ | ngrxPush">
@if ((routerConfig.includes('scalar') && minimized) || !minimized ) {
<div class="scalar-buttons" leftButtons></div>
}
@if ((routerConfig.includes('scalar') && minimized) || !minimized ) {
<span class="refresh-position" refresh>
<div class="scalar-buttons">
@if (routerConfig.includes('scalar')) {
<div class="table-graph-toggle" [class.maximized]="!minimized">
<i class="al-icon pointer al-color blue-400"
[smTooltip]="(metricValuesView$ | async)===false? 'Metric values': 'Graphs'"
[class]="(metricValuesView$ | async)===false? 'al-ico-table-view': 'al-ico-charts-view'"
(click)="toggleTableView()"
></i>
</div>
}
@if ((metricValuesView$ | async) === false && routerConfig.includes('scalar') && minimized) {
<sm-experiment-settings
[class.maximized]="!minimized"
[showSettings]="routerConfig.includes('scalar') && minimized"
(toggleSettings)="toggleSettingsBar()"
></sm-experiment-settings>
}
</div>
@if (!minimized){
<sm-refresh-button
class="light-theme"
(setAutoRefresh)="setAutoRefresh($event)"
></sm-refresh-button>
}
</span>
</sm-experiment-info-navbar>
}
</sm-experiment-info-navbar>
<div class="output-body" [class.minimized]="minimized" #scrollContainer>
<router-outlet class="output-outlet" (activate)="onActivate($event, scrollContainer)"></router-outlet>
</div>

View File

@@ -12,29 +12,62 @@ import {ExperimentCompareSharedModule} from '@common/experiments-compare/shared/
import {AngularSplitModule} from 'angular-split';
import {CommonLayoutModule} from '@common/layout/layout.module';
import {DebugImagesModule} from '@common/debug-images/debug-images.module';
import {ExperimentInfoExecutionComponent} from '@common/experiments/containers/experiment-info-execution/experiment-info-execution.component';
import {
ExperimentInfoExecutionComponent
} from '@common/experiments/containers/experiment-info-execution/experiment-info-execution.component';
import {MatSidenavModule} from '@angular/material/sidenav';
import {MatListModule} from '@angular/material/list';
import {ExperimentOutputComponent} from './containers/experiment-ouptut/experiment-output.component';
import {ExperimentInfoNavbarComponent} from './containers/experiment-info-navbar/experiment-info-navbar.component';
import {ExperimentInfoHyperParametersComponent} from '@common/experiments/containers/experiment-info-hyper-parameters/experiment-info-hyper-parameters.component';
import {ExperimentInfoArtifactItemComponent} from '@common/experiments/containers/experiment-info-artifact-item/experiment-info-artifact-item.component';
import {ExperimentGeneralInfoComponent} from '@common/experiments/dumb/experiment-general-info/experiment-general-info.component';
import {ExperimentArtifactItemViewComponent} from '@common/experiments/dumb/experiment-artifact-item-view/experiment-artifact-item-view.component';
import {ExperimentHyperParamsNavbarComponent} from '@common/experiments/dumb/experiment-hyper-params-navbar/experiment-hyper-params-navbar.component';
import {ExperimentExecutionSourceCodeComponent} from '@common/experiments/dumb/experiment-execution-source-code/experiment-execution-source-code.component';
import {ExperimentInfoEditDescriptionComponent} from '@common/experiments/dumb/experiment-info-edit-description/experiment-info-edit-description.component';
import {ExperimentOutputModelViewComponent} from '@common/experiments/dumb/experiment-output-model-view/experiment-output-model-view.component';
import {ExperimentInfoGeneralComponent} from '@common/experiments/containers/experiment-info-general/experiment-info-general.component';
import {
ExperimentInfoHyperParametersComponent
} from '@common/experiments/containers/experiment-info-hyper-parameters/experiment-info-hyper-parameters.component';
import {
ExperimentInfoArtifactItemComponent
} from '@common/experiments/containers/experiment-info-artifact-item/experiment-info-artifact-item.component';
import {
ExperimentGeneralInfoComponent
} from '@common/experiments/dumb/experiment-general-info/experiment-general-info.component';
import {
ExperimentArtifactItemViewComponent
} from '@common/experiments/dumb/experiment-artifact-item-view/experiment-artifact-item-view.component';
import {
ExperimentHyperParamsNavbarComponent
} from '@common/experiments/dumb/experiment-hyper-params-navbar/experiment-hyper-params-navbar.component';
import {
ExperimentExecutionSourceCodeComponent
} from '@common/experiments/dumb/experiment-execution-source-code/experiment-execution-source-code.component';
import {
ExperimentInfoEditDescriptionComponent
} from '@common/experiments/dumb/experiment-info-edit-description/experiment-info-edit-description.component';
import {
ExperimentOutputModelViewComponent
} from '@common/experiments/dumb/experiment-output-model-view/experiment-output-model-view.component';
import {
ExperimentInfoGeneralComponent
} from '@common/experiments/containers/experiment-info-general/experiment-info-general.component';
import {BaseClickableArtifactComponent} from '@common/experiments/dumb/base-clickable-artifact.component';
import {ExperimentModelsFormViewComponent} from '@common/experiments/dumb/experiment-models-form-view/experiment-models-form-view.component';
import {ExperimentArtifactsNavbarComponent} from '@common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component';
import {ExperimentInfoArtifactsComponent} from '@common/experiments/containers/experiment-info-aritfacts/experiment-info-artifacts.component';
import {ExperimentInfoHeaderComponent} from '@common/experiments/dumb/experiment-info-header/experiment-info-header.component';
import {ExperimentInfoTaskModelComponent} from '@common/experiments/containers/experiment-info-task-model/experiment-info-task-model.component';
import {ExperimentOutputScalarsComponent} from '@common/experiments/containers/experiment-output-scalars/experiment-output-scalars.component';
import {ExperimentInfoModelComponent} from '@common/experiments/containers/experiment-info-model/experiment-info-model.component';
import {ExperimentInfoHyperParametersFormContainerComponent} from '@common/experiments/containers/experiment-info-hyper-parameters-form-container/experiment-info-hyper-parameters-form-container.component';
import {
ExperimentModelsFormViewComponent
} from '@common/experiments/dumb/experiment-models-form-view/experiment-models-form-view.component';
import {
ExperimentInfoArtifactsComponent
} from '@common/experiments/containers/experiment-info-aritfacts/experiment-info-artifacts.component';
import {
ExperimentInfoHeaderComponent
} from '@common/experiments/dumb/experiment-info-header/experiment-info-header.component';
import {
ExperimentInfoTaskModelComponent
} from '@common/experiments/containers/experiment-info-task-model/experiment-info-task-model.component';
import {
ExperimentOutputScalarsComponent
} from '@common/experiments/containers/experiment-output-scalars/experiment-output-scalars.component';
import {
ExperimentInfoModelComponent
} from '@common/experiments/containers/experiment-info-model/experiment-info-model.component';
import {
ExperimentInfoHyperParametersFormContainerComponent
} from '@common/experiments/containers/experiment-info-hyper-parameters-form-container/experiment-info-hyper-parameters-form-container.component';
import {ExperimentOutputLogModule} from '@common/experiments/shared/experiment-output-log/experiment-output-log.module';
import {RouterModule} from '@angular/router';
import {ScrollingModule} from '@angular/cdk/scrolling';
@@ -51,7 +84,9 @@ import {MatTabsModule} from '@angular/material/tabs';
import {LabeledFormFieldDirective} from '@common/shared/directive/labeled-form-field.directive';
import {OverlayComponent} from '@common/shared/ui-components/overlay/overlay/overlay.component';
import {RefreshButtonComponent} from '@common/shared/components/refresh-button/refresh-button.component';
import {InfoHeaderStatusIconLabelComponent} from '@common/shared/experiment-info-header-status-icon-label/info-header-status-icon-label.component';
import {
InfoHeaderStatusIconLabelComponent
} from '@common/shared/experiment-info-header-status-icon-label/info-header-status-icon-label.component';
import {NAPipe} from '@common/shared/pipes/na.pipe';
import {SortPipe} from '@common/shared/pipes/sort.pipe';
import {safeAngularUrlParameterPipe} from '@common/shared/pipes/safeAngularUrlParameter.pipe';
@@ -86,8 +121,18 @@ import {MatExpansionModule} from '@angular/material/expansion';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {HesitateDirective} from '@common/shared/ui-components/directives/hesitate.directive';
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {
ShowTooltipIfEllipsisDirective
} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {SelectQueueModule} from '@common/experiments/shared/components/select-queue/select-queue.module';
import {PushPipe} from '@ngrx/component';
import {ExperimentHeaderComponent} from '@common/experiments/dumb/experiment-header/experiment-header.component';
import {
ExperimentOperationsLogComponent
} from '@common/experiments/dumb/experiment-operations-log/experiment-operations-log.component';
import {
ExperimentArtifactsNavbarComponent
} from '@common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component';
@NgModule({
@@ -152,13 +197,16 @@ import {SelectQueueModule} from '@common/experiments/shared/components/select-qu
HesitateDirective,
ShowTooltipIfEllipsisDirective,
SelectQueueModule,
PushPipe,
ExperimentHeaderComponent,
ExperimentOperationsLogComponent,
ExperimentArtifactsNavbarComponent,
],
declarations: [
ExperimentsComponent,
ExperimentInfoExecutionComponent,
ExperimentOutputComponent,
ExperimentInfoNavbarComponent,
ExperimentInfoNavbarComponent,
BaseClickableArtifactComponent,
ExperimentInfoHeaderComponent,
ExperimentInfoModelComponent,
@@ -171,7 +219,6 @@ import {SelectQueueModule} from '@common/experiments/shared/components/select-qu
ExperimentOutputScalarsComponent,
ExperimentInfoHyperParametersComponent,
ExperimentInfoHyperParametersFormContainerComponent,
ExperimentArtifactsNavbarComponent,
ExperimentInfoArtifactsComponent,
ExperimentHyperParamsNavbarComponent,
ExperimentInfoArtifactItemComponent,

View File

@@ -3,7 +3,6 @@ import {CommonModule} from '@angular/common';
import {ExperimentConverterService} from './services/experiment-converter.service';
import { ExperimentMenuComponent } from '@common/experiments/shared/components/experiment-menu/experiment-menu.component';
import {ExperimentMenuExtendedComponent} from '../containers/experiment-menu-extended/experiment-menu-extended.component';
import {ExperimentHeaderComponent} from '@common/experiments/dumb/experiment-header/experiment-header.component';
import {ExperimentExecutionParametersComponent} from '@common/experiments/dumb/experiment-execution-parameters/experiment-execution-parameters.component';
import {CloneDialogComponent} from '@common/experiments/shared/components/clone-dialog/clone-dialog.component';
import {ExperimentSystemTagsComponent} from '@common/experiments/shared/components/experiments-system-tags/experiment-system-tags.component';
@@ -79,6 +78,7 @@ import {DotsLoadMoreComponent} from '@common/shared/ui-components/indicators/dot
import {ExperimentCustomColsMenuComponent} from '@common/experiments/dumb/experiment-custom-cols-menu/experiment-custom-cols-menu.component';
import {SelectMetricForCustomColComponent} from '@common/experiments/dumb/select-metric-for-custom-col/select-metric-for-custom-col.component';
import {SelectHyperParamsForCustomColComponent} from '@common/experiments/dumb/select-hyper-params-for-custom-col/select-hyper-params-for-custom-col.component';
import {PushPipe} from '@ngrx/component';
export const experimentSyncedKeys = [
'view.projectColumnsSortOrder',
@@ -132,7 +132,6 @@ const DECLARATIONS = [
AbortAllChildrenDialogComponent,
ExperimentExecutionParametersComponent,
ExperimentsTableComponent,
ExperimentHeaderComponent,
ExperimentOutputPlotsComponent,
];
@@ -198,6 +197,7 @@ const DECLARATIONS = [
ExperimentCustomColsMenuComponent,
SelectMetricForCustomColComponent,
SelectHyperParamsForCustomColComponent,
PushPipe,
],
declarations : [...DECLARATIONS],
providers : [

View File

@@ -16,6 +16,7 @@ import {MatRadioModule} from '@angular/material/radio';
import {LoginComponent} from '@common/login/login/login.component';
import {NtkmeButtonModule} from '@ctrl/ngx-github-buttons';
import {SafePipe} from '@common/shared/pipes/safe.pipe';
import {PushPipe} from '@ngrx/component';
@NgModule({
declarations: [LoginComponent, SignupComponent],
@@ -33,7 +34,8 @@ import {SafePipe} from '@common/shared/pipes/safe.pipe';
MatInputModule,
MatRadioModule,
NtkmeButtonModule,
SafePipe
SafePipe,
PushPipe
]
})
export class LoginModule { }

View File

@@ -1,5 +1,5 @@
import { Component} from '@angular/core';
import {ModelMenuComponent} from '../../../../webapp-common/models/containers/model-menu/model-menu.component';
import {ModelMenuComponent} from '@common/models/containers/model-menu/model-menu.component';
@Component({
selector: 'sm-model-menu-extended',
@@ -7,8 +7,6 @@ import {ModelMenuComponent} from '../../../../webapp-common/models/containers/mo
styleUrls: ['../../../../webapp-common/models/containers/model-menu/model-menu.component.scss']
})
export class ModelMenuExtendedComponent extends ModelMenuComponent {
set contextMenu(data) {}
get contextMenu() {
return this;
}

View File

@@ -33,6 +33,7 @@ import {ButtonToggleComponent} from '@common/shared/ui-components/inputs/button-
import {NavbarItemComponent} from '@common/shared/ui-components/panel/navbar-item/navbar-item.component';
import {SaferPipe} from '@common/shared/pipes/safe.pipe';
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {DotsLoadMoreComponent} from '@common/shared/ui-components/indicators/dots-load-more/dots-load-more.component';
const _declarations = [
ProjectCardMenuExtendedComponent,
@@ -70,7 +71,8 @@ const _declarations = [
TooltipDirective,
ButtonToggleComponent,
NavbarItemComponent,
ShowTooltipIfEllipsisDirective
ShowTooltipIfEllipsisDirective,
DotsLoadMoreComponent
],
declarations: [..._declarations, PipelinesEmptyStateComponent],
exports: [..._declarations, PipelinesEmptyStateComponent]

View File

@@ -0,0 +1,5 @@
<div class="no-data-center">
<i class="al-icon al-ico-model-endpoints xxxl"></i>
<span class="message">MODEL ENDPOINTS WILL APPEAR HERE</span>
<span class="sub-message">Monitor models deployed with <a href="https://github.com/allegroai/clearml-serving" target="_blank">clearml-serving</a></span>
</div>

View File

@@ -0,0 +1,31 @@
@import "variables";
.no-data-center {
display: flex;
flex-direction: column;
justify-content: center;
gap: 24px;
align-items: center;
}
.al-icon {
color: $blue-600;
}
.message {
font-size: 20px;
font-weight: 500;
color: $blue-100;
}
.sub-message {
color: $blue-250;
}
a[href] {
color: $neon-yellow;
&:hover {
color: $neon-yellow;
}
}

View File

@@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import {RouterLink} from '@angular/router';
@Component({
selector: 'sm-serving-empty-state',
standalone: true,
imports: [
RouterLink
],
templateUrl: './serving-empty-state.component.html',
styleUrl: './serving-empty-state.component.scss'
})
export class ServingEmptyStateComponent {
}

View File

@@ -1,7 +0,0 @@
import {commonAuthReducer, initAuth} from '@common/core/reducers/common-auth-reducer';
import {createReducer} from '@ngrx/store';
export const authReducer = createReducer(
initAuth,
...commonAuthReducer
);

View File

@@ -1,6 +1,6 @@
<sm-dialog-template header="CREATE CREDENTIALS" iconClass="al-ico-access-key">
<sm-admin-dialog-template
[newCredential]="(newCredential$ | async)"
[newCredential]="newCredential()"
(updateLabel)="updateLabel($event)"
></sm-admin-dialog-template>
</sm-dialog-template>

View File

@@ -1,25 +1,32 @@
import {Component, Inject} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import {Store} from '@ngrx/store';
import {updateCredentialLabel} from '@common/core/actions/common-auth.actions';
import {OrganizationGetUserCompaniesResponseCompanies} from '~/business-logic/model/organization/organizationGetUserCompaniesResponseCompanies';
import {Observable} from 'rxjs';
import {CredentialKeyExt, selectNewCredential} from '@common/core/reducers/common-auth-reducer';
import {selectNewCredential} from '@common/core/reducers/common-auth-reducer';
import {DialogTemplateComponent} from '@common/shared/ui-components/overlay/dialog-template/dialog-template.component';
import {AdminDialogTemplateComponent} from '@common/settings/admin/admin-dialog-template/admin-dialog-template.component';
@Component({
selector: 'sm-create-credential-dialog',
templateUrl: './create-credential-dialog.component.html',
styleUrls: ['./create-credential-dialog.component.scss']
styleUrls: ['./create-credential-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
DialogTemplateComponent,
AdminDialogTemplateComponent
]
})
export class CreateCredentialDialogComponent {
public newCredential$: Observable<CredentialKeyExt>;
private store = inject(Store);
public data = inject<{
credentials: any;
workspace?: OrganizationGetUserCompaniesResponseCompanies
}>(MAT_DIALOG_DATA);
constructor(
@Inject(MAT_DIALOG_DATA) public data: {credentials: any; workspace?: OrganizationGetUserCompaniesResponseCompanies},
private store: Store
) {
this.newCredential$ = this.store.select(selectNewCredential);
}
protected newCredential = this.store.selectSignal(selectNewCredential);
updateLabel({credential, label}) {
this.store.dispatch(updateCredentialLabel({credential, label}));

View File

@@ -1,4 +1,4 @@
@if(!demo) {
@if(!demo() && supported()) {
<mat-slide-toggle
(change)="statsChange($event)"
[checked]="allowed()">Send anonymous usage data (Global setting)</mat-slide-toggle>

View File

@@ -0,0 +1,3 @@
mat-slide-toggle {
margin-bottom: 12px;
}

View File

@@ -1,7 +1,7 @@
import {Component, inject} from '@angular/core';
import {Component, computed, inject} from '@angular/core';
import {MatSlideToggle, MatSlideToggleChange} from '@angular/material/slide-toggle';
import {Store} from '@ngrx/store';
import {selectAllowed} from '~/core/reducers/usage-stats.reducer';
import {selectAllowed, selectStatsSupported} from '~/core/reducers/usage-stats.reducer';
import { updateUsageStats } from '~/core/actions/usage-stats.actions';
import {ConfigurationService} from '@common/shared/services/configuration.service';
@@ -16,10 +16,12 @@ import {ConfigurationService} from '@common/shared/services/configuration.servic
standalone: true
})
export class UsageStatsComponent {
private store =inject(Store);
private store = inject(Store);
private config = inject(ConfigurationService);
public shown = true;
public demo = ConfigurationService.globalEnvironment.demo;
public demo = computed(() => this.config.configuration().demo)
protected allowed = this.store.selectSignal(selectAllowed);
protected supported = this.store.selectSignal(selectStatsSupported);
statsChange(toggle: MatSlideToggleChange) {
this.store.dispatch(updateUsageStats({allowed: toggle.checked}));

View File

@@ -1,15 +1,11 @@
import { Component, OnInit } from '@angular/core';
import {Component, input} from '@angular/core';
@Component({
selector: 'sm-user-data',
templateUrl: './user-data.component.html',
styleUrls: ['./user-data.component.scss']
})
export class UserDataComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
export class UserDataComponent {
public userId = input<string>();
}

View File

@@ -3,7 +3,7 @@
<div class="title">USER PREFERENCES</div>
</header>
<main class="main">
<sm-profile-preferences class="section"></sm-profile-preferences>
<sm-profile-preferences></sm-profile-preferences>
<sm-profile-key-storage></sm-profile-key-storage>
</main>
</section>

View File

@@ -4,13 +4,16 @@
header {
padding: 20px 24px;
}
.main {
padding: 0 24px;
}
.section {
margin: 6px 0 24px;
padding-bottom: 12px;
}
.title {
color: $white;
font-size: 16px;

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import {ProfilePreferencesComponent} from '@common/settings/admin/profile-preferences/profile-preferences.component';
import {ProfileKeyStorageComponent} from '@common/settings/admin/profile-key-storage/profile-key-storage.component';
@Component({
selector: 'sm-webapp-configuration',
standalone: true,
imports: [
ProfilePreferencesComponent,
ProfileKeyStorageComponent,
],
templateUrl: './webapp-configuration.component.html',
styleUrl: './webapp-configuration.component.scss'
})
export class WebappConfigurationComponent {
}

View File

@@ -1,7 +1,7 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {ProfileNameComponent} from '@common/settings/admin/profile-name/profile-name.component';
import {WebappConfigurationComponent} from '@common/settings/webapp-configuration/webapp-configuration.component';
import {WebappConfigurationComponent} from '~/features/settings/containers/webapp-configuration/webapp-configuration.component';
import {WorkspaceConfigurationComponent} from '@common/settings/workspace-configuration/workspace-configuration.component';
import {SettingsComponent} from './settings.component';
import {CrumbTypeEnum} from '@common/layout/breadcrumbs/breadcrumbs.component';

View File

@@ -5,7 +5,7 @@ import { SettingsComponent } from '../settings/settings.component';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {SharedModule} from '~/shared/shared.module';
import {MatExpansionModule} from '@angular/material/expansion';
import {WebappConfigurationComponent} from '@common/settings/webapp-configuration/webapp-configuration.component';
import {WebappConfigurationComponent} from '~/features/settings/containers/webapp-configuration/webapp-configuration.component';
import {WorkspaceConfigurationComponent} from '@common/settings/workspace-configuration/workspace-configuration.component';
import {ProfileKeyStorageComponent} from '@common/settings/admin/profile-key-storage/profile-key-storage.component';
import {ProfileNameComponent} from '@common/settings/admin/profile-name/profile-name.component';
@@ -38,6 +38,8 @@ import {AdminDialogTemplateComponent} from '@common/settings/admin/admin-dialog-
import {TimeAgoPipe} from '@common/shared/pipes/timeAgo';
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {ProfilePreferencesComponent} from '@common/settings/admin/profile-preferences/profile-preferences.component';
import {PushPipe} from '@ngrx/component';
import {IdBadgeComponent} from '@common/shared/components/id-badge/id-badge.component';
@@ -48,13 +50,9 @@ import {ProfilePreferencesComponent} from '@common/settings/admin/profile-prefer
UserCredentialsComponent,
AdminFooterActionsComponent,
AdminCredentialTableComponent,
S3AccessComponent,
CreateCredentialDialogComponent,
AdminFooterComponent,
ProfileNameComponent,
ProfileKeyStorageComponent,
WorkspaceConfigurationComponent,
WebappConfigurationComponent,
RedactedArgumentsDialogComponent
],
imports: [
@@ -85,12 +83,17 @@ import {ProfilePreferencesComponent} from '@common/settings/admin/profile-prefer
TimeAgoPipe,
ShowTooltipIfEllipsisDirective,
ProfilePreferencesComponent,
S3AccessComponent,
CreateCredentialDialogComponent,
WebappConfigurationComponent,
ProfileKeyStorageComponent,
PushPipe,
IdBadgeComponent,
],
exports: [
UserCredentialsComponent,
AdminFooterComponent,
ProfileNameComponent,
WebappConfigurationComponent,
]
})
export class SettingsModule { }

View File

@@ -0,0 +1,3 @@
import {User} from '~/business-logic/model/users/user';
export type SettingsUser = User;

View File

@@ -52,6 +52,7 @@ import {MatInputModule} from '@angular/material/input';
import {OrchestrationComponent} from "~/features/workers-and-queues/orchestration.component";
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {RefreshButtonComponent} from '@common/shared/components/refresh-button/refresh-button.component';
import {PushPipe} from '@ngrx/component';
@NgModule({
imports: [
@@ -86,7 +87,8 @@ import {RefreshButtonComponent} from '@common/shared/components/refresh-button/r
TableModule,
MatInputModule,
ShowTooltipIfEllipsisDirective,
RefreshButtonComponent
RefreshButtonComponent,
PushPipe
],
declarations: [
OrchestrationComponent,

View File

@@ -2,8 +2,8 @@
<img src="assets/c-logo.svg?v=1" class="logo-a" alt="logo" width="64" height="64">
</div>
<ng-container *ngIf="currentUser">
<div #container class="items-container" [class.on-scrolling]="scrolling">
@if (currentUser()) {
<div #container class="items-container" [class.on-scrolling]="scrolling()">
<a class="item al-ico-home"
routerLink="/dashboard"
routerLinkActive="active"
@@ -20,22 +20,30 @@
routerLinkActive="active">
</a>
<a class="item al-ico-datasets"
[routerLink]="(defaultNestedModeForFeature$ | async)?.['datasets']? '/datasets/simple/*/projects' : '/datasets'"
[class.active]="(baseRouteConfig$ | async) ==='datasets'"
[routerLink]="defaultNestedModeForFeature()?.['datasets']? '/datasets/simple/*/projects' : '/datasets'"
[class.active]="baseRouteConfig() ==='datasets'"
smTooltip="DATASETS"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
</a>
<a class="item al-ico-pipelines"
[routerLink]="(defaultNestedModeForFeature$ | async)?.['pipelines']? '/pipelines/*/projects' : '/pipelines'"
[class.active]="(baseRouteConfig$ | async) ==='pipelines'"
[routerLink]="defaultNestedModeForFeature()?.['pipelines']? '/pipelines/*/projects' : '/pipelines'"
[class.active]="baseRouteConfig() ==='pipelines'"
smTooltip="PIPELINES"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
</a>
<a class="item al-ico-model-endpoints"
[routerLink]="'/endpoints/active'"
[class.active]="baseRouteConfig() ==='endpoints'"
smTooltip="MODEL ENDPOINTS"
data-id="modelEndpointsIcon"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
</a>
<a class="item al-ico-reports"
[routerLink]="(defaultNestedModeForFeature$ | async)?.['reports']? '/reports/*/projects' : '/reports'"
[class.active]="(baseRouteConfig$ | async) ==='reports'"
[routerLink]="defaultNestedModeForFeature()?.['reports']? '/reports/*/projects' : '/reports'"
[class.active]="baseRouteConfig() ==='reports'"
smTooltip="REPORTS"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
@@ -48,12 +56,15 @@
matTooltipPosition="right">
</a>
</div>
</ng-container>
<div *ngIf="currentUser" class="account">
<a *ngIf="environment.slackLink" class="item" [href]="environment.slackLink"
target="_blank"
smTooltip="Community support on Slack" [matTooltipShowDelay]="0" matTooltipPosition="right">
<i class="i-slack-mark-color"></i>
</a>
</div>
}
@if (currentUser()) {
<div class="account">
@if (environment().slackLink) {
<a class="item" [href]="environment().slackLink"
target="_blank"
smTooltip="Community support on Slack" [matTooltipShowDelay]="0" matTooltipPosition="right">
<i class="i-slack-mark-color"></i>
</a>
}
</div>
}

View File

@@ -1,55 +1,35 @@
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, ViewChild} from '@angular/core';
import {ChangeDetectionStrategy, Component, computed, ElementRef, inject, viewChild} from '@angular/core';
import {Store} from '@ngrx/store';
import {selectDefaultNestedModeForFeature, selectSelectedProjectId} from '@common/core/reducers/projects.reducer';
import {fromEvent, Observable} from 'rxjs';
import {ConfigurationService} from '@common/shared/services/configuration.service';
import {selectDefaultNestedModeForFeature} from '@common/core/reducers/projects.reducer';
import {fromEvent} from 'rxjs';
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
import {searchDeactivate} from '@common/dashboard-search/dashboard-search.actions';
import {selectRouterConfig} from '@common/core/reducers/router-reducer';
import {map} from 'rxjs/operators';
import {ConfigurationService} from '@common/shared/services/configuration.service';
import {selectFirstRouterConfig} from '@common/core/reducers/router-reducer';
import {toSignal} from '@angular/core/rxjs-interop';
@Component({
selector : 'sm-side-nav',
selector: 'sm-side-nav',
templateUrl: './side-nav.component.html',
styleUrls : ['./side-nav.component.scss'],
styleUrls: ['./side-nav.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SideNavComponent implements AfterViewInit {
public selectedProjectId$: Observable<any>;
currentUser: any;
environment = ConfigurationService.globalEnvironment;
public scrolling: boolean;
public defaultNestedModeForFeature$: Observable<{ [p: string]: boolean }>;
public baseRouteConfig$: Observable<string>;
export class SideNavComponent {
public store = inject(Store);
private configService = inject(ConfigurationService);
@ViewChild('container') container: ElementRef<HTMLDivElement>;
constructor(public store: Store<any>, private cdr: ChangeDetectorRef) {
this.selectedProjectId$ = this.store.select(selectSelectedProjectId);
this.defaultNestedModeForFeature$ = this.store.select(selectDefaultNestedModeForFeature);
this.store.select(selectCurrentUser).subscribe((res) => this.currentUser = res);
this.baseRouteConfig$ = this.store.select(selectRouterConfig).pipe(map(conf => conf?.[0]));
fromEvent(window, 'resize').subscribe(() => {
const scrolling = this.container.nativeElement.scrollHeight > this.container.nativeElement.clientHeight;
if (scrolling !== this.scrolling) {
this.scrolling = scrolling;
this.cdr.detectChanges();
}
this.scrolling = scrolling;
});
}
ngAfterViewInit(): void {
this.scrolling = this.container.nativeElement.scrollHeight > this.container.nativeElement.clientHeight;
}
private container = viewChild<ElementRef<HTMLDivElement>>('container');
protected baseRouteConfig = this.store.selectSignal(selectFirstRouterConfig);
protected environment = this.configService.configuration;
protected defaultNestedModeForFeature = this.store.selectSignal(selectDefaultNestedModeForFeature);
protected currentUser = this.store.selectSignal(selectCurrentUser);
private resize = toSignal(fromEvent(window, 'resize'));
protected scrolling = computed(() => {
this.resize();
return this.container()?.nativeElement.scrollHeight > this.container()?.nativeElement.clientHeight
});
public resetSearch() {
this.store.dispatch(searchDeactivate());
}
get guestUser(): boolean {
return this.currentUser && this.currentUser?.role === 'guest';
}
}

View File

@@ -10,6 +10,8 @@ export enum EntityTypeEnum {
dataset = 'version',
simpleDataset = 'dataset',
report = 'report',
endpoint = 'endpoint',
endpointsContainer = 'endpoints container'
}
export enum CircleTypeEnum {

View File

@@ -9,6 +9,7 @@ import {NotifierQueueService} from './services/notifier-queue.service';
import {NotifierConfigToken, NotifierService} from './services/notifier.service';
/**
* Injection Token for notifier options
*/
@@ -49,7 +50,7 @@ export const notifierDefaultConfigFactory = (): NotifierConfig => new NotifierCo
NotifierContainerComponent
],
imports: [
CommonModule
CommonModule,
],
providers: [
NotifierAnimationService,

View File

@@ -3,7 +3,7 @@
@font-face {
font-family: '#{$icomoon-font-family}';
src: url('./#{$icomoon-font-family}.ttf?luezm6') format('truetype');
src: url('./#{$icomoon-font-family}.ttf?h2mp1v') format('truetype');
font-weight: normal;
font-style: normal;
font-display: block;
@@ -23,9 +23,41 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.al-ico-close-circle {
&:before {
content: $al-ico-close-circle;
}
}
.al-ico-bullhorn {
&:before {
content: $al-ico-bullhorn;
}
}
.al-ico-view-horizontal {
&:before {
content: $al-ico-view-horizontal;
}
}
.al-ico-model-endpoints {
&:before {
content: $al-ico-model-endpoints;
}
}
.al-ico-vm {
&:before {
content: $al-ico-vm;
}
}
.al-ico-retry {
&:before {
content: $al-ico-retry;
}
}
.al-ico-link-off {
&:before {
content: $al-ico-link-off;
content: $al-ico-link-off;
}
}
.al-ico-policy {

View File

@@ -1,6 +1,12 @@
$icomoon-font-family: "trains" !default;
$icomoon-font-path: "fonts" !default;
$al-ico-close-circle: "\ea13";
$al-ico-bullhorn: "\ea12";
$al-ico-view-horizontal: "\ea11";
$al-ico-model-endpoints: "\ea08";
$al-ico-vm: "\ea0c";
$al-ico-retry: "\ea07";
$al-ico-link-off: "\ea05";
$al-ico-policy: "\e9d5";
$al-ico-info-group: "\e944";

View File

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M3.61,4.16l-1.61.8v14.04l1.61.8h0V4.2h0" fill="#8c3123"/>
<path d="M12.23,17.76l-8.62,2.04V4.16l8.62,2v11.6" fill="#e05243"/>
<path d="M8.34,14.57l3.66.46.02-.05v-5.95l-.02-.05-3.66.46v5.14" fill="#8c3123"/>
<path d="M12,17.78l8.39,2h0V4.18h0l-8.39,2v11.6" fill="#8c3123"/>
<path d="M15.66,14.57l-3.66.46v-6.05l3.66.46v5.14" fill="#e05243"/>
<path d="M15.66,6.95l-3.66.65-3.66-.66,3.66-.94,3.66.95" fill="#5e1f18"/>
<path d="M15.66,17.05l-3.66-.65-3.66.67,3.66.99,3.66-1.01" fill="#f2b0a9"/>
<path d="M8.34,6.95l3.66-.9h.03V.05l-.03-.05-3.66,1.82v5.14" fill="#8c3123"/>
<path d="M15.66,6.95l-3.66-.9V0l3.66,1.82v5.14" fill="#e05243"/>
<path d="M12,24l-3.66-1.82v-5.14l3.66.9.05.06v5.89l-.05.1" fill="#8c3123"/>
<path d="M12,24l3.66-1.82v-5.14l-3.66.9v6.05" fill="#e05243"/>
<path d="M20.39,4.16l1.61.8v14.04l-1.61.8V4.16" fill="#e05243"/>
</svg>

After

Width:  |  Height:  |  Size: 939 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<path d="M5.07,8.53c0,.22.02.4.07.53.05.13.11.28.19.43.03.05.04.1.04.14,0,.06-.04.12-.11.18l-.38.25c-.05.04-.11.05-.16.05-.06,0-.12-.03-.18-.08-.08-.09-.16-.19-.22-.28-.06-.1-.12-.22-.19-.35-.47.55-1.05.83-1.76.83-.5,0-.9-.14-1.2-.43-.29-.29-.44-.67-.44-1.15,0-.51.18-.92.54-1.23.37-.31.85-.47,1.47-.47.2,0,.41.02.63.05.22.03.45.08.69.13v-.44c0-.45-.1-.77-.28-.96-.19-.19-.51-.28-.98-.28-.21,0-.43.02-.65.08-.22.05-.44.12-.65.2-.1.04-.17.07-.21.08s-.07.02-.1.02c-.08,0-.13-.06-.13-.19v-.29c0-.1.01-.17.04-.21.03-.04.08-.08.17-.13.21-.11.46-.2.75-.27.29-.08.6-.11.93-.11.71,0,1.23.16,1.57.48.33.32.5.81.5,1.47v1.94h.01ZM2.64,9.44c.2,0,.4-.04.62-.11s.41-.2.57-.38c.1-.11.17-.24.2-.38s.06-.32.06-.52v-.25c-.17-.04-.36-.08-.55-.1s-.38-.04-.56-.04c-.4,0-.69.08-.89.24-.2.16-.29.39-.29.69,0,.28.07.49.22.63.14.15.35.22.63.22ZM7.45,10.08c-.11,0-.18-.02-.23-.06-.05-.04-.09-.12-.13-.23l-1.41-4.63c-.04-.12-.05-.2-.05-.24,0-.1.05-.15.14-.15h.59c.11,0,.19.02.23.06.05.04.08.12.12.23l1.01,3.96.93-3.96c.03-.12.07-.2.11-.23s.13-.06.24-.06h.48c.11,0,.19.02.24.06.05.04.09.12.11.23l.95,4.01,1.04-4.01c.04-.12.08-.2.12-.23.05-.04.13-.06.23-.06h.56c.1,0,.15.05.15.15,0,.03,0,.06-.01.1s-.02.08-.04.15l-1.44,4.63c-.04.12-.08.2-.13.23s-.13.06-.23.06h-.51c-.11,0-.19-.02-.24-.06s-.09-.12-.11-.24l-.93-3.86-.92,3.86c-.03.12-.07.2-.11.24s-.13.06-.24.06h-.51ZM15.14,10.25c-.31,0-.62-.04-.92-.11-.3-.07-.53-.15-.69-.24-.1-.05-.16-.11-.19-.17s-.04-.11-.04-.17v-.31c0-.13.05-.19.14-.19.04,0,.07,0,.11.02.04.01.09.04.15.06.2.09.43.16.66.21.24.05.47.07.71.07.38,0,.67-.07.87-.2s.31-.32.31-.57c0-.17-.05-.31-.16-.42-.11-.11-.31-.22-.6-.31l-.87-.27c-.44-.14-.76-.34-.96-.61-.2-.26-.3-.56-.3-.87,0-.25.05-.47.16-.66s.25-.36.43-.49c.18-.14.38-.24.62-.31.24-.07.49-.1.75-.1.13,0,.27,0,.4.02.14.02.26.04.39.07.12.03.23.06.34.1s.19.07.25.11c.08.05.14.1.18.15.04.05.05.11.05.2v.28c0,.13-.05.19-.14.19-.05,0-.13-.02-.23-.07-.34-.16-.72-.23-1.15-.23-.34,0-.61.05-.8.17s-.28.29-.28.53c0,.17.06.31.18.43s.34.23.66.33l.85.27c.43.14.74.33.93.57s.28.53.28.84c0,.26-.05.49-.16.69-.11.2-.25.38-.44.53-.19.15-.41.26-.66.34-.27.08-.55.13-.86.13h0Z" fill="#fff" />
<path d="M16.27,13.15c-1.97,1.45-4.83,2.23-7.29,2.23-3.45,0-6.56-1.28-8.9-3.39-.19-.17-.02-.4.2-.26,2.54,1.47,5.67,2.36,8.91,2.36,2.19,0,4.59-.45,6.79-1.39.33-.15.61.22.29.45ZM17.09,12.22c-.25-.32-1.66-.16-2.3-.08-.19.02-.22-.14-.05-.27,1.13-.79,2.98-.56,3.19-.3.22.27-.06,2.12-1.11,3.01-.16.14-.32.07-.25-.11.24-.59.77-1.93.52-2.24Z" fill="#f90"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M18.61,2.22l5.17,8.96c.29.51.29,1.13,0,1.63l-5.17,8.98c-.3.51-.85.82-1.44.82H6.83c-.59,0-1.14-.31-1.44-.82L.22,12.82c-.29-.51-.29-1.13,0-1.63L5.41,2.2c.29-.5.83-.82,1.41-.82h10.35c.6,0,1.14.32,1.44.84Z" fill="#0078d7"/>
<path d="M15.7,5.26H7.51c-.9,0-1.63.73-1.63,1.63v10.21c0,.9.73,1.63,1.63,1.63h8.98c.9,0,1.63-.73,1.63-1.63V7.71l-2.43-2.45ZM17.33,17.1c0,.45-.37.82-.82.82H7.51c-.45,0-.82-.37-.82-.82V6.9c0-.45.37-.82.82-.82h7.76v2.04h2.04l.02,8.98ZM10.04,8.64l-.86.25v-.67l1.66-.57h.08v3.77h-.87v-2.79ZM13.35,7.61c-.37-.02-.73.13-.97.41-.25.34-.37.76-.35,1.18v.69c-.03.42.1.84.35,1.18.52.54,1.37.55,1.91.04.01-.01.03-.03.04-.04.25-.34.37-.76.34-1.18v-.68c.03-.42-.1-.84-.35-1.18-.24-.28-.6-.44-.97-.42ZM13.8,10.02c0,.2-.03.41-.11.6-.06.12-.19.2-.33.19-.14,0-.28-.07-.34-.2-.08-.19-.12-.41-.11-.62v-.96c0-.2.03-.39.11-.57.06-.12.19-.19.33-.18.14,0,.28.07.34.2.08.19.12.4.11.61v.93ZM10.28,12.51c-.37-.02-.73.13-.97.41-.25.34-.37.76-.34,1.18v.69c-.03.42.09.84.34,1.18.24.28.6.43.98.41.37.02.73-.13.97-.41.25-.34.37-.75.34-1.17v-.69c.03-.42-.09-.84-.34-1.18-.24-.28-.6-.43-.97-.42ZM10.72,14.92c.01.2-.02.41-.11.6-.06.12-.19.2-.33.19-.14,0-.27-.07-.34-.2-.08-.19-.12-.41-.11-.62v-.96c0-.2.04-.39.12-.57.1-.18.33-.25.52-.15.06.03.12.09.15.15.08.19.12.4.11.61v.95ZM13.9,12.56h.08v3.77h-.87v-2.79l-.86.25v-.67l1.65-.57Z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<path d="M8.33,15.23c2.31-.41,4.22-.75,4.25-.75h.04s-2.18-2.61-2.18-2.61c-1.2-1.43-2.18-2.6-2.18-2.61,0-.01,2.26-6.22,2.27-6.24,0,0,1.54,2.64,3.72,6.42,2.04,3.54,3.72,6.46,3.74,6.48l.03.05H4.12l4.2-.74h0ZM0,14.44s1.03-1.79,2.29-3.97l2.29-3.96,2.66-2.24c1.46-1.23,2.66-2.24,2.67-2.24s-.01.05-.04.11-1.33,2.85-2.89,6.2l-2.84,6.09h-2.06C.93,14.44,0,14.44,0,14.44Z" fill="#0089d6"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<linearGradient id="d" x1="-303.64" y1="174.77" x2="-303.64" y2="170.21" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#4387fd"/>
<stop offset="1" stop-color="#4683ea"/>
</linearGradient>
</defs>
<path d="M5.21,21.6L.29,13.08c-.39-.67-.39-1.49,0-2.16L5.21,2.4c.39-.67,1.1-1.08,1.87-1.08h9.84c.77,0,1.48.41,1.87,1.08l4.92,8.52c.39.67.39,1.49,0,2.16l-4.92,8.52c-.39.67-1.1,1.08-1.87,1.08H7.08c-.77,0-1.48-.41-1.87-1.08h0Z" fill="url(#d)" />
<g clip-path="url(#e)">
<path d="M9.25,10.1l-1.31,1.31,1.12,1.12-1.11,1.96,8.19,8.19,2.01-.02,4.13-7.08-6.24-6.1-6.78.62Z" fill="#000" isolation="isolate" opacity=".07" />
</g>
<path d="M15.88,9.43h-7.77c-.13,0-.23.1-.23.23v1.61c0,.12.1.23.23.23h7.77c.13,0,.23-.1.23-.23v-1.61c0-.12-.1-.23-.23-.23M14.68,10.92c-.26,0-.46-.21-.46-.46,0-.26.21-.46.46-.46h0c.26,0,.46.21.46.46s-.21.46-.46.46h0M15.88,12.51h-7.77c-.13,0-.23.1-.23.23v1.61c0,.12.1.23.23.23h7.77c.13,0,.23-.1.23-.23v-1.61c0-.12-.1-.23-.23-.23M14.68,14.01c-.26,0-.46-.21-.46-.46,0-.26.21-.46.46-.46h0c.26-.01.47.19.48.45.01.26-.19.47-.45.48-.01,0-.02,0-.04,0" fill="#fff" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="66.47" height="10" viewBox="0 0 66.47 10">
<path d="M21.56.17h3.72v9.66h-3.72V.17ZM17.05.3l-7.56,4.05c-.11.06-.24.06-.35,0L1.59.3c-.17-.09-.35-.13-.54-.13h0C.5.14.04.54,0,1.08v8.74h3.72v-4.16c.02-.21.21-.35.42-.33.05,0,.1.02.15.05l4.24,2.28c.43.22.93.23,1.36.01l4.47-2.3c.18-.1.41-.04.51.14.03.04.04.09.05.15v4.17h3.72V1.08c-.04-.54-.5-.94-1.03-.91h0c-.19,0-.37.05-.54.13ZM43.85.17h-3.78v4.4c-.02.2-.21.35-.41.33-.05,0-.09-.02-.14-.04L29.74.28c-.15-.07-.32-.11-.49-.11h0c-.54-.03-1,.37-1.04.91v8.74h3.75v-4.39c.02-.21.21-.35.41-.33.05,0,.09.02.13.04l9.82,4.58c.15.07.32.11.49.11.54.03,1-.37,1.04-.91V.17ZM46.78,9.83V.17h1.71v9.66h-1.71ZM58.57,10c-4.61,0-7.88-1.92-7.88-5,0-3.06,3.29-5,7.88-5s7.9,1.92,7.9,5-3.23,5-7.9,5h0ZM58.57,1.28c-3.43,0-6.07,1.31-6.07,3.72s2.64,3.72,6.07,3.72,6.09-1.3,6.09-3.72-2.66-3.72-6.09-3.72Z" fill="#c72c48" />
</svg>

After

Width:  |  Height:  |  Size: 898 B

View File

@@ -22,7 +22,7 @@
[identifier]="'report-widget'"
[width]="400"
[xAxisType]="xaxis"
[isCompare]="true"
[isCompare]="$any(plotData)?.plotParsed?.data?.[0]?.type !== 'parcoords'"
[noMargins]="true"
[hideMaximize]="hideMaximize"
(maximizeClicked)="maximize()">

View File

@@ -17,7 +17,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base';
import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer';
import {getSignedUrl, setS3Credentials} from '@common/core/actions/common-auth.actions';
import {ConfigurationService} from '@common/shared/services/configuration.service';
import {_mergeVariants, convertMultiPlots, createMultiSingleValuesChart, mergeMultiMetricsGroupedVariant, prepareGraph, prepareMultiPlots, tryParseJson} from '@common/tasks/tasks.utils';
import {_mergeVariants, allowedMergedTypes, convertMultiPlots, createMultiSingleValuesChart, mergeMultiMetricsGroupedVariant, prepareGraph, prepareMultiPlots, tryParseJson} from '@common/tasks/tasks.utils';
import {selectSignedUrl} from '@common/core/reducers/common-auth-reducer';
import {loadExternalLibrary} from '@common/shared/utils/load-external-library';
import {ImageViewerComponent} from '@common/shared/debug-sample/image-viewer/image-viewer.component';
@@ -26,7 +26,6 @@ import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent';
import {SingleGraphComponent} from '@common/shared/single-graph/single-graph.component';
import {setCurrentDebugImage} from '@common/shared/debug-sample/debug-sample.actions';
import {isFileserverUrl} from '~/shared/utils/url';
import {MetricValueType, SelectedMetric} from '@common/experiments-compare/experiments-compare.constants';
import {
ExtraTask,
ParallelCoordinatesGraphComponent
@@ -60,7 +59,7 @@ export interface SelectedMetricVariant extends MetricVariantResult {
SingleGraphModule,
DebugSampleModule,
ParallelCoordinatesGraphComponent,
SingleValueSummaryTableComponent
SingleValueSummaryTableComponent,
]
})
export class AppComponent implements OnInit {
@@ -73,7 +72,7 @@ export class AppComponent implements OnInit {
public frame: DebugSample;
public plotLoaded: boolean;
private environment: Environment;
public activated: boolean = false;
public activated = false;
private searchParams: URLSearchParams;
private otherSearchParams: URLSearchParams;
public type: WidgetTypes;
@@ -82,7 +81,7 @@ export class AppComponent implements OnInit {
protected signIsNeeded$ = this.store.selectSignal(appFeature.selectSignIsNeeded);
protected noPermissions$ = this.store.selectSignal(appFeature.selectNoPermissions);
public isDarkTheme: boolean;
public externalTool: boolean = false;
public externalTool = false;
public parcoordsData: { experiments: ExtraTask[]; params: string[]; metrics: SelectedMetricVariant[] };
@ViewChild(SingleGraphComponent) 'singleGraph': SingleGraphComponent;
public singleValueData: EventsGetTaskSingleValueMetricsResponseValues[];
@@ -176,12 +175,12 @@ export class AppComponent implements OnInit {
if (plotParsed.data[0] && !plotParsed.data[0].name) {
plotParsed.data[0].name = plot.variant;
}
if (groupedPlots[metric] && ['scatter', 'bar'].includes(plotParsed?.data?.[0]?.type) && previousPlotIsMergable) {
if (groupedPlots[metric] && allowedMergedTypes.includes(plotParsed?.data?.[0]?.type) && previousPlotIsMergable) {
groupedPlots[metric].plotParsed = {...groupedPlots[metric].plotParsed, data: _mergeVariants(groupedPlots[metric].plotParsed.data, plotParsed.data)};
} else {
groupedPlots[metric] = {...plot, plotParsed};
}
previousPlotIsMergable = index > -1 || (index === -1 && ['scatter', 'bar'].includes(plotParsed.data[0]?.type));
previousPlotIsMergable = index > -1 || (index === -1 && allowedMergedTypes.includes(plotParsed.data[0]?.type));
});
});
return groupedPlots;
@@ -295,8 +294,9 @@ export class AppComponent implements OnInit {
if (isFileserverUrl(sample.url) && this.searchParams.get('company')) {
url.searchParams.append('tenant', this.searchParams.get('company'));
}
this.store.dispatch(getSignedUrl({url: url.toString()}));
this.store.select(selectSignedUrl(url.toString()))
const urlDecoded = decodeURI(url.toString()); // decodeURI and not decodeURIComponent
this.store.dispatch(getSignedUrl({url: urlDecoded}));
this.store.select(selectSignedUrl(urlDecoded))
.pipe(
filter(signed => !!signed?.signed),
map(({signed: signedUrl}) => signedUrl),

View File

@@ -1,16 +1,16 @@
import {ApplicationConfig, importProvidersFrom} from '@angular/core';
import {ApplicationConfig, importProvidersFrom, provideExperimentalZonelessChangeDetection} from '@angular/core';
import {StoreModule} from '@ngrx/store';
import {EffectsModule} from '@ngrx/effects';
import {appFeature} from '@common/clearml-applications/report-widgets/src/app/app.reducer';
import {authReducer} from '~/features/settings/containers/admin/auth.reducers';
import {authReducer} from '~/core/reducers/auth.reducers';
import {AppEffects} from '@common/clearml-applications/report-widgets/src/app/app.effects';
import {extCoreConfig} from '@common/clearml-applications/report-widgets/src/build';
import {provideHttpClient} from '@angular/common/http';
import {BaseAdminService} from '@common/settings/admin/base-admin.service';
import {ApiEventsService} from '~/business-logic/api-services/events.service';
import {SmApiRequestsService} from '~/business-logic/api-services/api-requests.service';
import {ColorHashService} from '@common/shared/services/color-hash/color-hash.service';
import {provideAnimations} from '@angular/platform-browser/animations';
import {BaseAdminService} from '@common/settings/admin/base-admin.service';
export const appConfig: ApplicationConfig = {
providers: [
@@ -18,11 +18,13 @@ export const appConfig: ApplicationConfig = {
StoreModule.forRoot({}),
StoreModule.forFeature(appFeature),
StoreModule.forFeature({name: 'auth', reducer: authReducer}),
// StoreModule.forFeature({name: 'users', reducer: usersReducer}),
EffectsModule.forRoot([AppEffects])
),
...extCoreConfig,
provideAnimations(),
provideHttpClient(),
provideExperimentalZonelessChangeDetection(),
BaseAdminService,
ApiEventsService,
SmApiRequestsService,

View File

@@ -1,6 +1,7 @@
import {Injectable} from '@angular/core';
import {selectSignedUrl} from '@common/core/reducers/common-auth-reducer';
import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';
import {Actions, createEffect, ofType} from '@ngrx/effects';
import {concatLatestFrom} from '@ngrx/operators';
import {
getParcoords,
getPlot,
@@ -14,7 +15,6 @@ import {
import {EMPTY, mergeMap, of, switchMap} from 'rxjs';
import {Store} from '@ngrx/store';
import {catchError, filter} from 'rxjs/operators';
import {BaseAdminService} from '@common/settings/admin/base-admin.service';
import {ReportsGetTaskDataResponse} from '~/business-logic/model/reports/reportsGetTaskDataResponse';
import {getSignedUrl, setSignedUrl} from '@common/core/actions/common-auth.actions';
import {SignResponse} from '@common/settings/admin/base-admin-utils';
@@ -25,6 +25,7 @@ import {requestFailed} from '@common/core/actions/http.actions';
import {Task} from '~/business-logic/model/tasks/task';
import {ScalarKeyEnum} from '~/business-logic/model/events/scalarKeyEnum';
import {ReportsApiMultiplotsResponse} from '@common/constants';
import {BaseAdminService} from '@common/settings/admin/base-admin.service';
@Injectable()

View File

@@ -79,23 +79,23 @@ $sm-neon: (
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theme-primary: mat.define-palette(mat.$indigo-palette);
$theme-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
$theme-primary: mat.m2-define-palette(mat.$m2-indigo-palette);
$theme-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);
$sm-dark-palette-primary: mat.define-palette($sm-purple);
$sm-dark-palette-accent: mat.define-palette($sm-purple, A200, A100, A400);
$sm-dark-palette-primary: mat.m2-define-palette($sm-purple);
$sm-dark-palette-accent: mat.m2-define-palette($sm-purple, A200, A100, A400);
$sm-neon-palette-primary: mat.define-palette($sm-neon);
$sm-neon-palette-accent: mat.define-palette($sm-neon, A200, A100, A400);
$sm-neon-palette-primary: mat.m2-define-palette($sm-neon);
$sm-neon-palette-accent: mat.m2-define-palette($sm-neon, A200, A100, A400);
// The warn palette is optional (defaults to red).
$theme-warn: mat.define-palette(mat.$red-palette);
$theme-warn: mat.m2-define-palette(mat.$m2-red-palette);
$font-family-base: 'Heebo', sans-serif; // Assumes the browser default, typically `16px`
$custom-typography: mat.define-typography-config(
$custom-typography: mat.m2-define-typography-config(
$font-family: $font-family-base
);
// Create the theme object. A theme consists of configurations for individual
// theming systems such as "color" or "typography".
$theme: mat.define-light-theme((
$theme: mat.m2-define-light-theme((
color: (
primary: $theme-primary,
accent: $theme-accent,
@@ -105,7 +105,7 @@ $theme: mat.define-light-theme((
density: -2
));
$sm-neon-theme: mat.define-dark-theme((
$sm-neon-theme: mat.m2-define-dark-theme((
color: (
primary: $sm-neon-palette-primary,
accent: $sm-neon-palette-accent,
@@ -114,7 +114,7 @@ $sm-neon-theme: mat.define-dark-theme((
density: -2
));
$sm-dark-theme: mat.define-dark-theme((
$sm-dark-theme: mat.m2-define-dark-theme((
color: (
primary: $sm-dark-palette-primary,
accent: $sm-dark-palette-accent,
@@ -549,3 +549,16 @@ input[type=number] {
margin: 6px auto 6px -74px;
}
}
.dark-theme .axis {
.domain {
stroke: $dark-grey-blue;
fill: $dark-grey-blue;
}
.tick line{
stroke: $dark-grey-blue;
fill: $dark-grey-blue;
stroke-opacity: 1;
}
}

View File

@@ -6,6 +6,8 @@ import {searchReducer} from './common-search.reducer';
import {ClickStopPropagationDirective} from '@common/shared/ui-components/directives/click-stop-propagation.directive';
import {SearchComponent} from '@common/shared/ui-components/inputs/search/search.component';
import {TooltipDirective} from '@common/shared/ui-components/indicators/tooltip/tooltip.directive';
import {PushPipe} from '@ngrx/component';
@NgModule({
imports: [
@@ -14,6 +16,7 @@ import {TooltipDirective} from '@common/shared/ui-components/indicators/tooltip/
ClickStopPropagationDirective,
SearchComponent,
TooltipDirective,
PushPipe,
],
declarations: [CommonSearchComponent],
exports : [CommonSearchComponent]

View File

@@ -1,10 +1,10 @@
<span class="search-container" [class.open]="isSearching$ | async">
<span class="search-container" [class.open]="isSearching$ | ngrxPush">
<sm-search
#searchComponent
class="search-header"
[class.regex-error]="regexError"
[value]="(searchQuery$ | async)?.original"
[placeholder]="searchPlaceholder$ | async"
[value]="(searchQuery$ | ngrxPush)?.original"
[placeholder]="searchPlaceholder$ | ngrxPush"
[hideIcons]="true"
[minimumChars]="minChars"
(focusout)="onSearchFocusOut()"
@@ -21,7 +21,7 @@
</sm-search>
</span>
<ng-container *ngIf="searchActive">
<i *ngIf="(isSearching$ | async); else searchIcon" class="al-icon al-ico-dialog-x pointer" data-id="closeSearchButton" (click)="clearSearch()" smClickStopPropagation></i>
<i *ngIf="(isSearching$ | ngrxPush); else searchIcon" class="al-icon al-ico-dialog-x pointer" data-id="closeSearchButton" (click)="clearSearch()" smClickStopPropagation></i>
<ng-template #searchIcon>
<i class="al-icon al-ico-search pointer" (click)="openSearch()" data-id="searchIcon"></i>
</ng-template>

View File

@@ -25,24 +25,24 @@
// your theme to them. This will ensure the correct values for your app are included.
//@include mat.all-component-bases(/* TODO(v17): pass $your-theme here */);
$custom-typography: mat.define-typography-config(
$custom-typography: mat.m2-define-typography-config(
$font-family: $font-family-base
);
@include mat.typography-hierarchy($custom-typography);
@include mat.all-component-typographies($custom-typography);
$allegro-palette-primary: mat.define-palette($mat-allegro);
$allegro-palette-accent: mat.define-palette($mat-allegro, A400, A100, A400);
$allegro-palette-primary: mat.m2-define-palette($mat-allegro);
$allegro-palette-accent: mat.m2-define-palette($mat-allegro, A400, A100, A400);
$sm-palette-primary: mat.define-palette($sm-purple);
$sm-palette-accent: mat.define-palette($sm-purple, A100, A200, A400);
$sm-palette-warn: mat.define-palette(mat.$purple-palette);
$sm-palette-primary: mat.m2-define-palette($sm-purple);
$sm-palette-accent: mat.m2-define-palette($sm-purple, A100, A200, A400);
$sm-palette-warn: mat.m2-define-palette(mat.$m2-purple-palette);
$sm-neon-palette-primary: mat.define-palette($sm-neon);
$sm-neon-palette-accent: mat.define-palette($sm-neon, A200, A100, A400);
$sm-neon-palette-primary: mat.m2-define-palette($sm-neon);
$sm-neon-palette-accent: mat.m2-define-palette($sm-neon, A200, A100, A400);
$dark-theme: mat.define-dark-theme((
$dark-theme: mat.m2-define-dark-theme((
color: (
primary: $allegro-palette-primary,
accent: $allegro-palette-accent
@@ -51,7 +51,7 @@ $dark-theme: mat.define-dark-theme((
density: -2
));
$light-theme: mat.define-light-theme((
$light-theme: mat.m2-define-light-theme((
color: (
primary: $allegro-palette-primary,
accent: $allegro-palette-accent
@@ -59,7 +59,7 @@ $light-theme: mat.define-light-theme((
typography: $custom-typography,
));
$sm-theme: mat.define-light-theme((
$sm-theme: mat.m2-define-light-theme((
color: (
primary: $sm-palette-primary,
accent: $sm-palette-accent,
@@ -68,7 +68,7 @@ $sm-theme: mat.define-light-theme((
typography: $custom-typography
));
$sm-neon-theme: mat.define-dark-theme((
$sm-neon-theme: mat.m2-define-dark-theme((
color: (
primary: $sm-neon-palette-primary,
accent: $sm-neon-palette-accent,
@@ -108,6 +108,7 @@ $sm-neon-theme: mat.define-dark-theme((
@include mat.checkbox-theme($dark-theme);
@include mat.list-theme($dark-theme);
@include mat.stepper-theme($dark-theme);
@include mat.tabs-theme($sm-neon-theme);
--mdc-typography-body1-letter-spacing: 0;
--mdc-typography-button-letter-spacing: 0;
@@ -149,6 +150,7 @@ $sm-neon-theme: mat.define-dark-theme((
@include mat.checkbox-color($sm-theme);
@include mat.list-color($light-theme);
@include mat.stepper-color($sm-theme);
@include mat.tabs-color($sm-theme);
--mdc-typography-body1-letter-spacing: 0;
--mdc-typography-button-letter-spacing: 0;
@@ -163,10 +165,10 @@ $sm-neon-theme: mat.define-dark-theme((
--mdc-linear-progress-track-height: 12px;
}
.mat-mdc-radio-button, .mat-mdc-radio-button.mat-accent {
--mdc-radio-disabled-selected-icon-color: #{$blue-300};
--mdc-radio-disabled-unselected-icon-color: #{$blue-300};
}
//.mat-mdc-radio-button, .mat-mdc-radio-button.mat-accent {
// --mdc-radio-disabled-selected-icon-color: #{$blue-300};
// --mdc-radio-disabled-unselected-icon-color: #{$blue-300};
//}
.mat-drawer {
border-right: solid 1px $blue-100;
@@ -401,9 +403,9 @@ mat-expansion-panel {
width: 16px;
height: 16px;
.mdc-radio__inner-circle {
border-width: 8px;
}
//.mdc-radio__inner-circle {
// border-width: 8px;
//}
}
.mat-ripple.mat-radio-ripple {
@@ -509,6 +511,11 @@ button {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&.inline {
display: inline-block;
vertical-align: middle;
}
}
body .ui-state-highlight .card {
@@ -1042,8 +1049,7 @@ button.btn.button-outline-dark {
}
.cdk-drag-preview.form-group-drag {
padding: 8px 16px 32px 16px;
border-radius: 4px;
overflow: hidden;
border: solid 1px $cloudy-blue;
background-color: white;
}

View File

@@ -16,6 +16,7 @@ export const ICONS = {
MINUS: 'fa-minus',
CHART: 'al-ico-info-max',
QUEUED: 'al-ico-manage-queue',
RETRY: 'al-ico-retry',
ENQUEUE: 'al-ico-enqueue',
DEQUEUE: 'al-ico-dequeue',
IN_PROGRESS: 'fa-hourglass-half',

View File

@@ -1,6 +1,7 @@
import {createAction, props} from '@ngrx/store';
import {CredentialKeyExt, Credentials} from '../reducers/common-auth-reducer';
import {GetCurrentUserResponseUserObjectCompany} from '~/business-logic/model/users/getCurrentUserResponseUserObjectCompany';
import {SettingsUser} from '~/features/settings/settings.util';
export const AUTH_PREFIX = 'AUTH_';
@@ -16,7 +17,12 @@ export const updateS3Credential = createAction(
);
export const createCredential = createAction(
AUTH_PREFIX + 'CREATE_CREDENTIAL (API)',
props<{workspace: GetCurrentUserResponseUserObjectCompany; openCredentialsPopup?: boolean; label?: string; userId?: string}>()
props<{workspace: GetCurrentUserResponseUserObjectCompany; openCredentialsPopup?: boolean; label?: string; user?: SettingsUser}>()
);
export const createCredentialPopup = createAction(
AUTH_PREFIX + 'CREATE_CREDENTIAL POPUP',
props<{workspace: GetCurrentUserResponseUserObjectCompany; openCredentialsPopup?: boolean; label?: string; user?: SettingsUser}>()
);
export const updateCredentialLabel = createAction(

View File

@@ -95,6 +95,7 @@ export const firstLogin = createAction(
);
export const neverShowPopupAgain = createAction(VIEW_PREFIX + 'NEVER_SHOW_POPUP_AGAIN', props<{ popupId: string; reset?: boolean }>());
export const neverShowChangesAgain = createAction(VIEW_PREFIX + 'NEVER_SHOW_CHANGES_AGAIN', props<{ version: string }>());
export const setRedactedArguments = createAction(VIEW_PREFIX + 'SET_REDACTED_ARGUMENTS', props<{ redactedArguments: { key: string } [] }>());
export const setHideRedactedArguments = createAction(VIEW_PREFIX + 'SET_SHOW_REDACTED_ARGUMENTS', props<{ hide: boolean }>());
export const plotlyReady = createAction(VIEW_PREFIX + '[plotly ready]');

View File

@@ -47,7 +47,7 @@ export const setTypeBreadcrumbs = createAction(
);
export const setWorkspaceNeutral = createAction(
BREADCRUMBS_PREFIX + 'SET_TYPE_BREADCRUMBS',
BREADCRUMBS_PREFIX + 'set workspace neutral',
props<{ neutral: boolean }>()
);

View File

@@ -18,23 +18,30 @@ import {
selectSignedUrls
} from '@common/core/reducers/common-auth-reducer';
import {EMPTY, forkJoin, of} from 'rxjs';
import {S3AccessDialogData, S3AccessResolverComponent} from '@common/layout/s3-access-resolver/s3-access-resolver.component';
import {
S3AccessDialogData,
S3AccessDialogResult,
S3AccessResolverComponent
} from '@common/layout/s3-access-resolver/s3-access-resolver.component';
import {MatDialog} from '@angular/material/dialog';
import {isGoogleCloudUrl, SignResponse} from '@common/settings/admin/base-admin-utils';
import {isFileserverUrl} from '~/shared/utils/url';
import {selectRouterQueryParams} from '@common/core/reducers/router-reducer';
import {concatLatestFrom} from '@ngrx/operators';
import {selectExtraCredentials} from '~/core/reducers/auth.reducers';
import {ErrorService} from '@common/shared/services/error.service';
@Injectable()
export class CommonAuthEffects {
private signAfterPopup: Action[] = [];
private openPopup: { [bucketName: string]: boolean } = {};
private openPopup: Record<string, boolean> = {};
constructor(
private actions: Actions,
private credentialsApi: ApiAuthService,
private store: Store,
private adminService: AdminService,
private errorService: ErrorService,
private matDialog: MatDialog
) {
}
@@ -60,7 +67,7 @@ export class CommonAuthEffects {
revokeCredential = createEffect(() => this.actions.pipe(
ofType(authActions.credentialRevoked),
concatLatestFrom(() => [
this.store.select(selectRouterQueryParams).pipe(map(params => params.userId)),
this.store.select(selectRouterQueryParams).pipe(map(params => params.userId))
]),
mergeMap(([action, userId]) => this.credentialsApi.authRevokeCredentials(
{access_key: action.accessKey}, {userId}).pipe(
@@ -79,18 +86,18 @@ export class CommonAuthEffects {
createCredential = createEffect(() => this.actions.pipe(
ofType(authActions.createCredential),
concatLatestFrom(() => [
this.store.select(selectRouterQueryParams).pipe(map(params => params.userId)),
this.store.select(selectRouterQueryParams).pipe(map(params => params.userId))
]),
mergeMap(([action, userId]) =>
this.credentialsApi.authCreateCredentials({label: action.label},
{userId}).pipe(
this.credentialsApi.authCreateCredentials({label: action.label}, {userId}).pipe(
mergeMap(({credentials}) => [
authActions.addCredential({newCredential: credentials, workspaceId: action.workspace?.id}),
...(action.openCredentialsPopup && [authActions.createCredentialPopup({...action})] || []),
deactivateLoader(action.type)
]),
catchError(error => [
requestFailed(error),
setServerError(error, null, 'Unable to create credentials'),
setServerError(error, null, this.errorService.getErrorMsg(error?.error)),
authActions.addCredential({newCredential: {}, workspaceId: action.workspace?.id}),
deactivateLoader(action.type)])
))
@@ -99,7 +106,7 @@ export class CommonAuthEffects {
updateCredentialLabel = createEffect(() => this.actions.pipe(
ofType(authActions.updateCredentialLabel),
concatLatestFrom(() => this.store.select(selectRouterQueryParams).pipe(map(params => params.userId))),
// eslint-disable-next-line @typescript-eslint/naming-convention
mergeMap(([action, userId]) =>
this.credentialsApi.authEditCredentials({access_key: action.credential.access_key, label: action.label},
{userId}).pipe(
@@ -124,6 +131,12 @@ export class CommonAuthEffects {
signUrl = createEffect(() => this.actions.pipe(
ofType(authActions.getSignedUrl),
switchMap(action => this.store.select(selectExtraCredentials)
.pipe(
filter(c => c !== null),
map(() => action)
)
),
filter(action => !!action.url),
mergeMap(action =>
of(action).pipe(
@@ -158,11 +171,25 @@ export class CommonAuthEffects {
return this.actions.pipe(
ofType(authActions.signUrls),
filter(action => action.sign.length > 0),
concatLatestFrom(() => this.store.select(selectSignedUrls)),
switchMap(action => this.store.select(selectExtraCredentials)
.pipe(
filter(c => c !== null),
map(() => action)
)
),
concatLatestFrom(() => [
this.store.select(selectSignedUrls)
]),
mergeMap(([action, prevSigns]) =>
forkJoin(action.sign.map(req =>
of(action).pipe(
switchMap(() => this.adminService.signUrlIfNeeded(req.url, req.config, prevSigns[req.url])),
concatLatestFrom(() =>
this.store.select(selectSignedUrl(req.url))
),
switchMap(([, signedUrl]) => (!signedUrl?.expires || signedUrl.expires < (new Date()).getTime() ||
(isFileserverUrl(req.url) && req.config?.disableCache) ||
(req.config?.error && isGoogleCloudUrl(req.url) && !signedUrl.signed.includes('X-Amz-Signature'))
) ? this.adminService.signUrlIfNeeded(req.url, req.config, prevSigns[req.url]) : of({type: 'none'})),
map(res => ({res, orgUrl: req.url}))
)
)).pipe(
@@ -189,8 +216,10 @@ export class CommonAuthEffects {
})];
}
case 'sign':
return setSignedUrls({signed: groups['sign'].map((res: {res: SignResponse, orgUrl: string}) =>
({url: res.orgUrl, signed: res.res.signed, expires: res.res.expires}))});
return setSignedUrls({
signed: groups['sign'].map((res: { res: SignResponse, orgUrl: string }) =>
({url: res.orgUrl, signed: res.res.signed, expires: res.res.expires}))
});
default:
return null;
}
@@ -199,8 +228,8 @@ export class CommonAuthEffects {
.flat();
})
)
),
)
)
);
});
s3popup = createEffect(() => this.actions.pipe(
@@ -215,23 +244,26 @@ export class CommonAuthEffects {
if (action?.credentials?.Bucket) {
this.openPopup[action.credentials.Bucket] = true;
}
return this.matDialog.open(S3AccessResolverComponent, {data: action as S3AccessDialogData, maxWidth: 700}).afterClosed().pipe(
concatLatestFrom(() => this.store.select(selectS3BucketCredentialsBucketCredentials)),
switchMap(([data, bucketCredentials]) => {
const actions = [...this.signAfterPopup];
this.signAfterPopup = [];
if (data) {
if (!data.success) {
const emptyCredentials = bucketCredentials.find((cred => cred?.Bucket === data.bucket)) === undefined;
const dontAskAgainForBucketName = emptyCredentials ? '' : data.bucket + data.endpoint;
return [authActions.cancelS3Credentials({dontAskAgainForBucketName})];
return this.matDialog.open<S3AccessResolverComponent, S3AccessDialogData, S3AccessDialogResult>(
S3AccessResolverComponent, {data: action, maxWidth: 700}
).afterClosed()
.pipe(
concatLatestFrom(() => this.store.select(selectS3BucketCredentialsBucketCredentials)),
switchMap(([data, bucketCredentials]) => {
const actions = [...this.signAfterPopup];
this.signAfterPopup = [];
if (data) {
if (!data.success) {
const emptyCredentials = bucketCredentials.find((cred => cred?.Bucket === data.Bucket)) === undefined;
const dontAskAgainForBucketName = emptyCredentials ? '' : data.Bucket + data.Endpoint;
return [authActions.cancelS3Credentials({dontAskAgainForBucketName})];
}
return [authActions.saveS3Credentials({newCredential: data}), ...actions];
}
return [authActions.saveS3Credentials({newCredential: data}), ...actions];
}
return actions;
}),
finalize(() => action?.credentials?.Bucket && delete this.openPopup[action.credentials.Bucket])
);
return actions;
}),
finalize(() => action?.credentials?.Bucket && delete this.openPopup[action.credentials.Bucket])
);
})
));
}

Some files were not shown because too many files have changed in this diff Show More