Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ export declare interface ReleaseReady {
/**
* Represents the status of a Lab Server release.
*/
export type ReleaseStatus = 'Latest' | 'Current' | 'Deprecated' | '';
export type ReleaseStatus =
| 'LATEST'
| 'CURRENT'
| 'LATEST_AND_CURRENT'
| 'VERSION_STATUS_UNSPECIFIED'
| '';

/**
* Configuration for a specific Lab Server release.
Expand Down Expand Up @@ -174,7 +179,19 @@ export declare interface DecommissionHostResponse {}
/**
* Response for UpdatePassThroughFlags API.
*/
export declare interface UpdatePassThroughFlagsResponse {}
export declare interface UpdatePassThroughFlagsResponse {
readonly success: boolean;
readonly error?: {
readonly code:
| 'CODE_UNSPECIFIED'
| 'PERMISSION_DENIED'
| 'INVALID_FLAGS'
| 'HOST_NOT_FOUND'
| 'WARPGATE_ERROR'
| 'UNKNOWN';
readonly message?: string;
};
}

/**
* Response for those rollout action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class FakeHostService extends HostService {
const scenario = MOCK_HOST_SCENARIOS.find((s) => s.hostName === hostName);
if (scenario && scenario.overview) {
scenario.overview.labServer.passThroughFlags = flags;
return of({});
return of({success: true});
} else {
return throwError(
() =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @fileoverview Mock host scenario in SHARED device config mode. */

import {DeviceConfig} from '../../../models/device_config_models';
import {PreflightLabServerReleaseResponse} from '../../../models/host_action';
import {HostConfig} from '../../../models/host_config_models';
import {MockHostScenario} from '../models';
import {
Expand Down Expand Up @@ -33,6 +34,25 @@ const HOST_CONFIG: HostConfig = {
},
};

const PREFLIGHT_RESPONSE: PreflightLabServerReleaseResponse = {
ready: {
versions: [
{
name: '[RELEASE] 4.358.0 mobileharness_lab_server',
version: '4.358.0',
status: 'LATEST_AND_CURRENT',
buildTime: new Date(Date.now() - 3600000 * 24).toISOString(),
},
{
name: '[RELEASE] 4.357.0 mobileharness_lab_server',
version: '4.357.0',
status: '',
buildTime: new Date(Date.now() - 3600000 * 48).toISOString(),
},
],
},
};

export const SCENARIO_HOST_SHARED_MODE: MockHostScenario = {
hostName: 'host-shared-mode.example.com',
scenarioName: '3. Shared Mode',
Expand All @@ -44,4 +64,5 @@ export const SCENARIO_HOST_SHARED_MODE: MockHostScenario = {
},
defaultDeviceConfig: null, // No default in SHARED mode
actions: createHostActions('RUNNING', true),
releaseResponse: PREFLIGHT_RESPONSE,
};
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function createDefaultReleaseResponse(): PreflightLabServerReleaseRespons
{
name: 'mobileharness_lab_server',
version: 'v4.349.0',
status: 'Latest',
status: 'LATEST',
buildTime: '2024-03-15 21:30:00',
ports: [{protocol: 'grpc', portNumber: 9994}],
releaseDetails: {
Expand Down Expand Up @@ -270,7 +270,7 @@ export function createDefaultReleaseResponse(): PreflightLabServerReleaseRespons
{
name: 'release_configs',
version: 'v4.357.0',
status: 'Current',
status: 'CURRENT',
buildTime: '2025-03-13 12:00:00',
ports: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
type="host"
width="64rem"
height="80vh"
maxHeight="90vh"
maxHeight="80vh"
footerType="normal">
<div header-prefix class="header-icon-wrapper">
<mat-icon>tune</mat-icon>
Expand Down Expand Up @@ -112,7 +112,8 @@
</div>

<!-- Right Column: Presets -->
<div class="flags-dialog-presets-col">
<!-- TODO: qiupingf - remove hidden when the RPC is ready. -->
<div class="flags-dialog-presets-col hidden">
<div class="presets-header">
<div class="header-title">
<mat-icon>auto_awesome</mat-icon>
Expand Down Expand Up @@ -161,9 +162,6 @@ <h3>Quick Presets</h3>

<ng-template #actionsTemplate>
<div class="footer-actions">
@if (errorMessage()) {
<span class="error-text">{{ errorMessage() }}</span>
}
<button class="secondary-button" (click)="discardChanges()" [disabled]="!isDirty() || isSaving()">Discard Changes</button>
<button class="primary-button" (click)="save()" [disabled]="!isDirty() || isSaving()">
@if (isSaving()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@
}
}
}

&.hidden {
display: none;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import {MatInputModule} from '@angular/material/input';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {take} from 'rxjs/operators';

import {PopularFlag} from '../../../../../core/models/host_action';
import {SnackBarService} from '@deviceinfra/app/shared/services/snackbar_service';
import {
PopularFlag,
UpdatePassThroughFlagsResponse,
} from '../../../../../core/models/host_action';
import {HOST_SERVICE} from '../../../../../core/services/host/host_service';
import {Dialog} from '../../../../../shared/components/config_common/dialog/dialog';

Expand Down Expand Up @@ -58,14 +62,14 @@ export class FlagsDialog implements OnInit {
readonly data = inject<FlagsDialogData>(MAT_DIALOG_DATA);
private readonly dialogRef = inject(MatDialogRef<FlagsDialog>);
private readonly hostService = inject(HOST_SERVICE);
private readonly snackBarService = inject(SnackBarService);

readonly isListMode = signal(true);
readonly currentFlagsArray = signal<string[]>([]);
readonly initialFlagsArray = signal<string[]>([]);
readonly presets = signal<PopularFlag[]>([]);
readonly isLoadingPresets = signal(false);
readonly isSaving = signal(false);
readonly errorMessage = signal('');
readonly addInput = signal('');
readonly filterText = signal('');
readonly rawTextFlags = signal('');
Expand Down Expand Up @@ -196,13 +200,23 @@ export class FlagsDialog implements OnInit {
}

appendPreset(preset: PopularFlag) {
const newFlags = this.splitFlags(preset.cmd);
if (this.isListMode()) {
const newFlags = this.splitFlags(preset.cmd);
this.currentFlagsArray.update((flags) => [...flags, ...newFlags]);
this.currentFlagsArray.update((flags) => {
const currentSet = new Set(flags);
const flagsToAppend = newFlags.filter((f) => !currentSet.has(f));
return [...flags, ...flagsToAppend];
});
} else {
this.rawTextFlags.update((text) => {
const currentFlags = this.splitFlags(text);
const currentSet = new Set(currentFlags);
const flagsToAppend = newFlags.filter((f) => !currentSet.has(f));
if (flagsToAppend.length === 0) return text;
const trimmed = text.trim();
return trimmed ? `${trimmed} ${preset.cmd}` : preset.cmd;
return trimmed
? `${trimmed} ${flagsToAppend.join(' ')}`
: flagsToAppend.join(' ');
});
}
}
Expand All @@ -228,19 +242,27 @@ export class FlagsDialog implements OnInit {
}

this.isSaving.set(true);
this.errorMessage.set('');

this.hostService
.updatePassThroughFlags(this.data.hostName, finalString)
.pipe(take(1))
.subscribe({
next: () => {
this.isSaving.set(false);
this.dialogRef.close(finalString);
next: (response: UpdatePassThroughFlagsResponse) => {
if (response.success) {
this.isSaving.set(false);
this.dialogRef.close(finalString);
} else {
this.isSaving.set(false);
const msg =
response.error?.message ||
response.error?.code ||
'Failed to save flags';
this.snackBarService.showError(msg);
}
},
error: (err: Error) => {
error: () => {
this.isSaving.set(false);
this.errorMessage.set(err.message || 'Failed to save flags');
this.snackBarService.showError('Failed to save flags');
},
});
}
Expand Down
Loading
Loading