Skip to content

Commit

Permalink
NAS-132278: Convert installed app to custom app (#11109)
Browse files Browse the repository at this point in the history
Co-authored-by: Boris Vasilenko <bvasilenko@ixsystems.com>
  • Loading branch information
bvasilenko and Boris Vasilenko authored Dec 2, 2024
1 parent 4b7a1ac commit 8f8ac70
Show file tree
Hide file tree
Showing 93 changed files with 339 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/app/interfaces/api/api-job-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface ApiJobDirectory {
'app.delete': { params: AppDeleteParams; response: boolean };
'app.upgrade': { params: AppUpgradeParams; response: App };
'app.rollback': { params: AppRollbackParams; response: App };
'app.convert_to_custom': { params: [appName: string]; response: App };

// CloudBackup
'cloud_backup.sync': { params: [id: number, params?: { dry_run: boolean }]; response: void };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<h3 mat-card-title>
{{ 'Application Info' | translate }}
</h3>
<div>
<div class="actions">
<button
id="edit-app"
mat-button
Expand All @@ -13,17 +13,31 @@ <h3 mat-card-title>
{{ 'Edit' | translate }}
</button>

@if (app()?.upgrade_available) {
<button
mat-icon-button
ixTest="app-info-menu"
[matMenuTriggerFor]="appInfoMenu"
>
<ix-icon name="more_vert"></ix-icon>
</button>
<mat-menu #appInfoMenu="matMenu">
<button
*ixRequiresRoles="requiredRoles"
id="update-app"
mat-button
mat-menu-item
[ixTest]="[app()?.name, 'update']"
[disabled]="!app().upgrade_available"
(click)="updateButtonPressed()"
>
{{ 'Update' | translate }}
</button>
}
<button
*ixRequiresRoles="requiredRoles"
mat-menu-item
[ixTest]="[app()?.name, 'convert']"
[disabled]="app().custom_app"
(click)="openConvertDialog()"
>{{ 'Convert to custom app' | translate}}</button>
</mat-menu>
</div>
</mat-card-header>
<mat-card-content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
}
}

.actions {
align-items: center;
display: flex;
}

.info-container {
display: flex;
flex-direction: row;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { EventEmitter } from '@angular/core';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatMenuHarness } from '@angular/material/menu/testing';
import { Router } from '@angular/router';
import { Spectator } from '@ngneat/spectator';
import { createComponentFactory, mockProvider } from '@ngneat/spectator/jest';
Expand Down Expand Up @@ -94,6 +95,7 @@ describe('AppInfoCardComponent', () => {
installedApps$: of([]),
}),
mockProvider(DialogService, {
confirm: jest.fn(() => of(true)),
jobDialog: jest.fn(() => ({
afterClosed: () => of(null),
})),
Expand All @@ -104,6 +106,7 @@ describe('AppInfoCardComponent', () => {
mockProvider(RedirectService),
mockAuth(),
mockApi([
mockJob('app.convert_to_custom'),
mockJob('app.upgrade'),
mockJob('app.delete'),
mockCall('app.rollback_versions', ['1.2.1']),
Expand Down Expand Up @@ -157,19 +160,26 @@ describe('AppInfoCardComponent', () => {
]);
});

it('shows header', () => {
it('shows header', async () => {
setupTest(fakeApp);
spectator.detectChanges();
expect(spectator.query('mat-card-header h3')).toHaveText('Application Info');
expect(spectator.query('mat-card-header button#edit-app')).toHaveText('Edit');
expect(spectator.query('mat-card-header button#update-app')).toHaveText('Update');

const menu = await loader.getHarness(MatMenuHarness.with({ selector: '[ixTest="app-info-menu"]' }));
await menu.open();

const menuItems = await menu.getItems();
expect(menuItems).toHaveLength(2);
expect(await menuItems[0].getText()).toContain('Update');
expect(await menuItems[1].getText()).toContain('Convert to custom app');
});

it('opens upgrade app dialog when Update button is pressed', async () => {
setupTest(fakeApp);

const updateButton = await loader.getHarness(MatButtonHarness.with({ text: 'Update' }));
await updateButton.click();
const menu = await loader.getHarness(MatMenuHarness.with({ selector: '[ixTest="app-info-menu"]' }));
await menu.clickItem({ text: 'Update' });

expect(spectator.inject(MatDialog).open).toHaveBeenCalledWith(AppUpgradeDialogComponent, {
maxWidth: '750px',
Expand All @@ -182,6 +192,16 @@ describe('AppInfoCardComponent', () => {
});
});

it('converts app to custom when Convert button is pressed', async () => {
setupTest(fakeApp);

const menu = await loader.getHarness(MatMenuHarness.with({ selector: '[ixTest="app-info-menu"]' }));
await menu.clickItem({ text: 'Convert to custom app' });

expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled();
expect(spectator.inject(ApiService).job).toHaveBeenLastCalledWith('app.convert_to_custom', ['test-user-app-name']);
});

it('navigates to app edit page when Edit button is pressed', async () => {
setupTest(fakeApp);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import {
signal,
WritableSignal,
} from '@angular/core';
import { MatButton } from '@angular/material/button';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard, MatCardActions, MatCardContent, MatCardHeader, MatCardTitle,
} from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog';
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
import { MatTooltip } from '@angular/material/tooltip';
import { Router, RouterLink } from '@angular/router';
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
Expand Down Expand Up @@ -58,6 +59,10 @@ import { ApiService } from 'app/services/websocket/api.service';
MatCardHeader,
MatCardTitle,
MatButton,
MatIconButton,
MatMenu,
MatMenuItem,
MatMenuTrigger,
TestDirective,
RequiresRolesDirective,
MatCardContent,
Expand Down Expand Up @@ -213,4 +218,24 @@ export class AppInfoCardComponent {
untilDestroyed(this),
).subscribe();
}

openConvertDialog(): void {
const appName = this.app().name;
this.dialogService.confirm({
title: this.translate.instant('Convert to custom app'),
message: this.translate.instant(
'You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.',
{ appName },
),
buttonText: this.translate.instant('Convert'),
}).pipe(
filter(Boolean),
switchMap(() => this.dialogService.jobDialog(
this.api.job('app.convert_to_custom', [appName]),
{ title: this.translate.instant('Convert to custom app') },
).afterClosed()),
this.errorHandler.catchError(),
untilDestroyed(this),
).subscribe();
}
}
3 changes: 3 additions & 0 deletions src/assets/i18n/af.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/az.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/be.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/bn.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/br.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@
"Controls whether audit messages will be generated for the share. <br><br> <b>Note</b>: Auditing may not be enabled if SMB1 support is enabled for the server.": "",
"Controls whether the user used for SSH/SSH+NETCAT replication will have passwordless sudo enabled to execute zfs commands on the remote host. If not checked, <i>zfs allow</i> must be used to grant non-user permissions to perform ZFS tasks. Mounting ZFS filesystems by non-root still would not be possible due to Linux restrictions.": "",
"Controls whether the volume snapshot devices under /dev/zvol/⟨pool⟩ are hidden or visible. The default value is hidden.": "",
"Convert": "",
"Convert to custom app": "",
"Cooling": "",
"Copied to clipboard": "",
"Copies": "",
Expand Down Expand Up @@ -5080,6 +5082,7 @@
"Yes": "",
"Yes I understand the risks": "",
"Yesterday": "",
"You are about to convert {appName} to a custom app. This will allow you to edit its yaml file directly.\nWarning. This operation cannot be undone.": "",
"You are trying to open:<br>\n{url}<br><br>\nBecause HTTP to HTTPS redirect is enabled in settings your browser will force HTTPS connection for this URL.<br>\nThis may create issues if app does not support secure connections.<br>\n<br>\nYou can try opening app url in an incognito mode.<br>\nAlternatively you can disable redirect in Settings, clear browser cache and try again.": "",
"You are using an insecure connection. Switch to HTTPS for secure access.": "",
"You can also vote for new features <a target=\"_blank\" href=\"https://forums.truenas.com/feature-requests\">on our forum.</a>": "",
Expand Down
Loading

0 comments on commit 8f8ac70

Please sign in to comment.