Skip to content

Commit

Permalink
Merge branch 'master' into NAS-132626-alt
Browse files Browse the repository at this point in the history
  • Loading branch information
RehanY147 committed Jan 13, 2025
2 parents 2f9091c + 1db0212 commit 406325b
Show file tree
Hide file tree
Showing 1,364 changed files with 9,166 additions and 7,300 deletions.
4 changes: 4 additions & 0 deletions scripts/ui-search/extract-ui-search-elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
* 2️⃣. Create .elements.ts config file near the .component.html file ~ [pools-dashboard.elements.ts]
*
* Example of creating a new searchable element:
*
* !! It's required to add anchor to the element where you do not specify hierarchy explicitly !!
* You will get TS error if it's not provided correctly
*
export const customSearchableElements = {
hierarchy: [T('System'), T('Advanced Settings'), T('Access')],
Expand Down
6 changes: 4 additions & 2 deletions scripts/ui-search/parse-ui-search-elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,16 @@ export function parseUiSearchElements(
function createUiSearchElement(
cheerioRoot$: (selector: CheerioElement | string) => { attr: (attr: string) => string },
element: CheerioElement,
elementConfig: UiSearchableElement,
elementConfig: Record<string, UiSearchableElement>,
parentKey: keyof UiSearchableElement,
childKey: keyof UiSearchableElement,
componentProperties: Record<string, string>,
): UiSearchableElement {
try {
const parent = (elementConfig?.[parentKey] || elementConfig) as UiSearchableElement;
const child = parent?.elements?.[childKey] || parent?.manualRenderElements?.[childKey] || {};
const child = parent?.elements?.[childKey]
|| parent?.manualRenderElements?.[childKey]
|| {} as UiSearchableElement;

const hierarchy = [...parent?.hierarchy || [], ...child?.hierarchy || []];
const visibleTokens = [...parent?.visibleTokens || [], ...child?.visibleTokens || []];
Expand Down
8 changes: 4 additions & 4 deletions src/app/admin.routes.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Routes } from '@angular/router';
import { marker as T } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslationsLoadedGuard } from 'app/core/guards/translations-loaded.guard';
import { WebSocketConnectionGuard } from 'app/core/guards/websocket-connection.guard';
import { AuthGuardService } from 'app/modules/auth/auth-guard.service';
import { TwoFactorGuardService } from 'app/modules/auth/two-factor-guard.service';
import { TranslationsLoadedGuard } from 'app/modules/language/translations/translations-loaded.guard';
import { AdminLayoutComponent } from 'app/modules/layout/admin-layout/admin-layout.component';
import { WebSocketConnectionGuard } from 'app/modules/websocket/websocket-connection.guard';
import { PlotterService } from 'app/pages/reports-dashboard/services/plotter.service';
import { SmoothPlotterService } from 'app/pages/reports-dashboard/services/smooth-plotter.service';
import { AuthGuardService } from 'app/services/auth/auth-guard.service';
import { TwoFactorGuardService } from 'app/services/auth/two-factor-guard.service';

export const adminRoutes: Routes = [
{
Expand Down
10 changes: 5 additions & 5 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { Router, NavigationEnd, RouterOutlet } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { filter, tap } from 'rxjs';
import { WINDOW } from 'app/helpers/window.helper';
import { AuthService } from 'app/services/auth/auth.service';
import { LayoutService } from 'app/modules/layout/layout.service';
import { PingService } from 'app/modules/websocket/ping.service';
import { DetectBrowserService } from 'app/services/detect-browser.service';
import { LayoutService } from 'app/services/layout.service';
import { PingService } from 'app/services/websocket/ping.service';
import { WebSocketStatusService } from 'app/services/websocket-status.service';

@UntilDestroy()
@Component({
Expand All @@ -24,13 +24,13 @@ export class AppComponent implements OnInit {
constructor(
public title: Title,
private router: Router,
private authService: AuthService,
private wsStatus: WebSocketStatusService,
private detectBrowser: DetectBrowserService,
private layoutService: LayoutService,
private pingService: PingService,
@Inject(WINDOW) private window: Window,
) {
this.authService.isAuthenticated$.pipe(untilDestroyed(this)).subscribe((isAuthenticated) => {
this.wsStatus.isAuthenticated$.pipe(untilDestroyed(this)).subscribe((isAuthenticated) => {
this.isAuthenticated = isAuthenticated;
});
this.title.setTitle('TrueNAS - ' + this.window.location.hostname);
Expand Down
4 changes: 2 additions & 2 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Routes } from '@angular/router';
import { marker as T } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslationsLoadedGuard } from 'app/core/guards/translations-loaded.guard';
import { WebSocketConnectionGuard } from 'app/core/guards/websocket-connection.guard';
import { TranslationsLoadedGuard } from 'app/modules/language/translations/translations-loaded.guard';
import { BlankLayoutComponent } from 'app/modules/layout/blank-layout/blank-layout.component';
import { WebSocketConnectionGuard } from 'app/modules/websocket/websocket-connection.guard';
import { SigninComponent } from 'app/pages/signin/signin.component';

export const rootRoutes: Routes = [
Expand Down
10 changes: 6 additions & 4 deletions src/app/core/testing/classes/mock-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import {
} from 'app/interfaces/api/api-job-directory.interface';
import { ApiEventTyped } from 'app/interfaces/api-message.interface';
import { Job } from 'app/interfaces/job.interface';
import { ApiService } from 'app/services/websocket/api.service';
import { SubscriptionManagerService } from 'app/services/websocket/subscription-manager.service';
import { WebSocketHandlerService } from 'app/services/websocket/websocket-handler.service';
import { ApiService } from 'app/modules/websocket/api.service';
import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service';
import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service';
import { WebSocketStatusService } from 'app/services/websocket-status.service';

/**
* Better than just expect.anything() because it allows null and undefined.
Expand All @@ -47,10 +48,11 @@ export class MockApiService extends ApiService {

constructor(
wsHandler: WebSocketHandlerService,
wsStatus: WebSocketStatusService,
subscriptionManager: SubscriptionManagerService,
translate: TranslateService,
) {
super(wsHandler, subscriptionManager, translate);
super(wsHandler, wsStatus, subscriptionManager, translate);

this.call = jest.fn();
this.job = jest.fn();
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/testing/classes/mock-auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { Role } from 'app/enums/role.enum';
import { LoggedInUser } from 'app/interfaces/ds-cache.interface';
import { AuthService } from 'app/services/auth/auth.service';
import { AuthService } from 'app/modules/auth/auth.service';

@Injectable()
export class MockAuthService extends AuthService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { MockEnclosureConfig } from 'app/core/testing/mock-enclosure/interfaces/
import { MockEnclosureGenerator } from 'app/core/testing/mock-enclosure/mock-enclosure-generator.utils';
import { ApiCallMethod, ApiCallParams, ApiCallResponse } from 'app/interfaces/api/api-call-directory.interface';
import { SystemInfo } from 'app/interfaces/system-info.interface';
import { ApiService } from 'app/services/websocket/api.service';
import { SubscriptionManagerService } from 'app/services/websocket/subscription-manager.service';
import { WebSocketHandlerService } from 'app/services/websocket/websocket-handler.service';
import { ApiService } from 'app/modules/websocket/api.service';
import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service';
import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service';
import { WebSocketStatusService } from 'app/services/websocket-status.service';

@Injectable({
providedIn: 'root',
Expand All @@ -20,10 +21,11 @@ export class MockEnclosureApiService extends ApiService {

constructor(
wsManager: WebSocketHandlerService,
wsStatus: WebSocketStatusService,
subscriptionManager: SubscriptionManagerService,
translate: TranslateService,
) {
super(wsManager, subscriptionManager, translate);
super(wsManager, wsStatus, subscriptionManager, translate);

console.warn('MockEnclosureApiService is in effect. Some calls will be mocked');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ export class MockEnclosureGenerator {
platform: `TRUENAS-${this.config.controllerModel}`,
system_product: `TRUENAS-${this.config.controllerModel}`,
}
: null,
: undefined,
};
}

private addEnclosure(model: EnclosureModel): void {
const enclosure = enclosureMocks.find((mock) => mock.model === model);

if (!enclosure) {
throw new Error(`Enclosure model ${model} is not supported`);
}

this.enclosures.push(enclosure);
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/core/testing/utils/empty-api.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getMissingInjectionErrorFactory, getMissingInjectionErrorObservable } from 'app/core/testing/utils/missing-injection-factories';
import { ApiService } from 'app/services/websocket/api.service';
import { ApiService } from 'app/modules/websocket/api.service';

export class EmptyApiService {
readonly clearSubscriptions$ = getMissingInjectionErrorObservable(ApiService.name);
Expand Down
3 changes: 1 addition & 2 deletions src/app/core/testing/utils/empty-auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { getMissingInjectionErrorFactory, getMissingInjectionErrorObservable } from 'app/core/testing/utils/missing-injection-factories';
import { AuthService } from 'app/services/auth/auth.service';
import { AuthService } from 'app/modules/auth/auth.service';

export class EmptyAuthService {
readonly authToken$ = getMissingInjectionErrorObservable(AuthService.name);
readonly isAuthenticated$ = getMissingInjectionErrorObservable(AuthService.name);
readonly user$ = getMissingInjectionErrorObservable(AuthService.name);
readonly isSysAdmin$ = getMissingInjectionErrorObservable(AuthService.name);
readonly userTwoFactorConfig$ = getMissingInjectionErrorObservable(AuthService.name);
Expand Down
16 changes: 11 additions & 5 deletions src/app/core/testing/utils/mock-api.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import {
import { ApiCallMethod } from 'app/interfaces/api/api-call-directory.interface';
import { ApiJobDirectory, ApiJobMethod } from 'app/interfaces/api/api-job-directory.interface';
import { Job } from 'app/interfaces/job.interface';
import { ApiService } from 'app/services/websocket/api.service';
import { SubscriptionManagerService } from 'app/services/websocket/subscription-manager.service';
import { WebSocketHandlerService } from 'app/services/websocket/websocket-handler.service';
import { ApiService } from 'app/modules/websocket/api.service';
import { SubscriptionManagerService } from 'app/modules/websocket/subscription-manager.service';
import { WebSocketHandlerService } from 'app/modules/websocket/websocket-handler.service';
import { WebSocketStatusService } from 'app/services/websocket-status.service';

/**
* This is a sugar syntax for creating simple api mocks.
Expand Down Expand Up @@ -49,11 +50,12 @@ export function mockApi(
{
provide: ApiService,
useFactory: (
wsStatus: WebSocketStatusService,
wsHandler: WebSocketHandlerService,
translate: TranslateService,
) => {
const subscriptionManager = {} as SubscriptionManagerService;
const mockApiService = new MockApiService(wsHandler, subscriptionManager, translate);
const mockApiService = new MockApiService(wsHandler, wsStatus, subscriptionManager, translate);
(mockResponses || []).forEach((mockResponse) => {
if (mockResponse.type === MockApiResponseType.Call) {
mockApiService.mockCall(mockResponse.method, mockResponse.response);
Expand All @@ -66,7 +68,11 @@ export function mockApi(
});
return mockApiService;
},
deps: [WebSocketHandlerService, TranslateService],
deps: [WebSocketStatusService, WebSocketHandlerService, TranslateService],
},
{
provide: WebSocketStatusService,
useValue: ({} as WebSocketStatusService),
},
{
provide: MockApiService,
Expand Down
17 changes: 11 additions & 6 deletions src/app/core/testing/utils/mock-auth.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import { MockAuthService } from 'app/core/testing/classes/mock-auth.service';
import { AccountAttribute } from 'app/enums/account-attribute.enum';
import { Role } from 'app/enums/role.enum';
import { LoggedInUser } from 'app/interfaces/ds-cache.interface';
import { AuthService } from 'app/services/auth/auth.service';
import { AuthService } from 'app/modules/auth/auth.service';
import { DialogService } from 'app/modules/dialog/dialog.service';
import { ApiService } from 'app/modules/websocket/api.service';
import { ErrorHandlerService } from 'app/services/error-handler.service';
import { TokenLastUsedService } from 'app/services/token-last-used.service';
import { ApiService } from 'app/services/websocket/api.service';
import { WebSocketHandlerService } from 'app/services/websocket/websocket-handler.service';
import { WebSocketStatusService } from 'app/services/websocket-status.service';

export const dummyUser = {
privilege: {
Expand Down Expand Up @@ -40,12 +42,15 @@ export function mockAuth(
provide: AuthService,
useFactory: () => {
const mockService = new MockAuthService(
createSpyObject(WebSocketHandlerService, {
isConnected$: of(true),
}),
createSpyObject(Store),
createSpyObject(ApiService),
createSpyObject(TokenLastUsedService),
createSpyObject(WebSocketStatusService, {
isConnected$: of(true),
isAuthenticated$: of(false),
}),
createSpyObject(DialogService),
createSpyObject(ErrorHandlerService),
createSpyObject(Window),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Store } from '@ngrx/store';
import { WINDOW } from 'app/helpers/window.helper';
import { IxSimpleChanges } from 'app/interfaces/simple-changes.interface';
import { headerHeight, footerHeight } from 'app/modules/layout/admin-layout/admin-layout.component.const';
import { LayoutService } from 'app/services/layout.service';
import { LayoutService } from 'app/modules/layout/layout.service';
import { AppState } from 'app/store';
import { waitForAdvancedConfig } from 'app/store/system-config/system-config.selectors';

Expand Down
2 changes: 1 addition & 1 deletion src/app/directives/has-role/has-role.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
import { BehaviorSubject } from 'rxjs';
import { HasRoleDirective } from 'app/directives/has-role/has-role.directive';
import { Role } from 'app/enums/role.enum';
import { AuthService } from 'app/services/auth/auth.service';
import { AuthService } from 'app/modules/auth/auth.service';

describe('HasRolesDirective', () => {
let spectator: SpectatorHost<HasRoleDirective>;
Expand Down
2 changes: 1 addition & 1 deletion src/app/directives/has-role/has-role.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { distinctUntilChanged } from 'rxjs';
import { Role } from 'app/enums/role.enum';
import { AuthService } from 'app/services/auth/auth.service';
import { AuthService } from 'app/modules/auth/auth.service';

@UntilDestroy()
@Directive({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isEqual } from 'lodash-es';
import { take } from 'rxjs';
import { HasAccessDirective } from 'app/directives/has-access/has-access.directive';
import { Role } from 'app/enums/role.enum';
import { AuthService } from 'app/services/auth/auth.service';
import { AuthService } from 'app/modules/auth/auth.service';

@UntilDestroy()
@Directive({
Expand Down
7 changes: 4 additions & 3 deletions src/app/directives/ui-search.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ export class UiSearchDirective implements OnInit, OnDestroy {
const hierarchyItem = this.config().hierarchy?.[this.config().hierarchy.length - 1] || '';
const isSingleWord = hierarchyItem.trim().split(/\s+/).length === 1;

if (isSingleWord && this.config().synonyms?.length > 0) {
return this.config().synonyms.reduce((best, synonym) => {
const synonyms = this.config().synonyms;
if (isSingleWord && synonyms && Number(synonyms?.length) > 0) {
return synonyms.reduce((best, synonym) => {
const synonymWordCount = synonym.trim().split(/\s+/).length;
const bestWordCount = best.trim().split(/\s+/).length;
return synonymWordCount > bestWordCount ? synonym : best;
Expand Down Expand Up @@ -73,7 +74,7 @@ export class UiSearchDirective implements OnInit, OnDestroy {
private highlightElementAnchor(elementAnchor: string): void {
setTimeout(() => {
const rootNode = this.elementRef.nativeElement.getRootNode() as HTMLElement;
const anchorRef: HTMLElement = rootNode?.querySelector(`#${elementAnchor}`);
const anchorRef: HTMLElement | null = rootNode?.querySelector(`#${elementAnchor}`);

if (anchorRef) {
this.highlightAndClickElement(anchorRef);
Expand Down
20 changes: 20 additions & 0 deletions src/app/enums/virtualization.enum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { marker as T } from '@biesbjerg/ngx-translate-extract-marker';
import { iconMarker } from 'app/modules/ix-icon/icon-marker.util';

export enum VirtualizationType {
Container = 'CONTAINER',
Expand All @@ -10,6 +11,21 @@ export const virtualizationTypeLabels = new Map<VirtualizationType, string>([
[VirtualizationType.Vm, T('VM')],
]);

export const virtualizationTypeIcons = [
{
value: VirtualizationType.Container,
icon: iconMarker('mdi-linux'),
label: T('Container'),
description: T('Linux Only'),
},
{
value: VirtualizationType.Vm,
icon: iconMarker('mdi-laptop'),
label: T('VM'),
description: T('Any OS'),
},
];

export enum VirtualizationStatus {
Running = 'RUNNING',
Stopped = 'STOPPED',
Expand Down Expand Up @@ -82,3 +98,7 @@ export const virtualizationNicTypeLabels = new Map<VirtualizationNicType, string
[VirtualizationNicType.Bridged, T('Bridged Adaptors')],
[VirtualizationNicType.Macvlan, T('MAC VLAN')],
]);

export enum VirtualizationSource {
Image = 'IMAGE',
}
10 changes: 7 additions & 3 deletions src/app/helpers/get-credentials-creation-source.utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { CredentialType, Credentials } from 'app/interfaces/credential-type.interface';

export function getCredentialsCreationSource(credentials: Credentials): string {
export function getCredentialsCreationSource(credentials: Credentials | null): string {
if (!credentials?.type) {
return '';
}

if ([CredentialType.UnixSocket, CredentialType.LoginPassword, CredentialType.Token].includes(credentials.type)) {
return credentials.data.username;
return credentials.data?.username || '';
}
if (credentials.type === CredentialType.ApiKey) {
return credentials.data.api_key;
return credentials.data?.api_key || '';
}
return '';
}
2 changes: 1 addition & 1 deletion src/app/helptext/sharing/smb/smb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const helptextSharingSmb = {
placeholder_fsrvp: T('Enable FSRVP'),
tooltip_fsrvp: T(
'Enable support for the File Server Remote VSS Protocol \
(<a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fsrvp" target="_blank">FSVRP</a>). \
(<a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fsrvp" target="_blank">FSRVP</a>). \
This protocol allows RPC clients to manage snapshots for a specific SMB share. \
The share path must be a dataset mountpoint. Snapshots have the prefix \
<code>fss-</code> followed by a snapshot creation timestamp. A snapshot must have \
Expand Down
Loading

0 comments on commit 406325b

Please sign in to comment.