Skip to content

Commit 4b2cfbc

Browse files
authored
Merge pull request #859 from Jigsaw-Code/fortuna-fix
Fix server removal
2 parents 403f082 + 3bee7cd commit 4b2cfbc

File tree

5 files changed

+123
-77
lines changed

5 files changed

+123
-77
lines changed

src/server_manager/web_app/app.spec.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe('App', () => {
8080
expect(managedServers.length).toEqual(1);
8181
const manualServers = await manualServerRepo.listServers();
8282
expect(manualServers.length).toEqual(2);
83-
appRoot.getServerView('');
83+
await appRoot.getServerView('');
8484
const serverList = appRoot.serverList;
8585

8686
console.log(`managedServers.length: ${managedServers.length}`);
@@ -114,7 +114,8 @@ describe('App', () => {
114114
await app.start();
115115
await app.createDigitalOceanServer('fakeRegion');
116116
expect(appRoot.currentPage).toEqual('serverView');
117-
expect(appRoot.getServerView(appRoot.selectedServerId).selectedPage).toEqual('progressView');
117+
const view = await appRoot.getServerView(appRoot.selectedServerId);
118+
expect(view.selectedPage).toEqual('progressView');
118119
});
119120

120121
it('shows progress screen when starting with DigitalOcean servers still being created',
@@ -128,7 +129,8 @@ describe('App', () => {
128129
localStorage.setItem(LAST_DISPLAYED_SERVER_STORAGE_KEY, server.getId());
129130
await app.start();
130131
expect(appRoot.currentPage).toEqual('serverView');
131-
expect(appRoot.getServerView(appRoot.selectedServerId).selectedPage).toEqual('progressView');
132+
const view = await appRoot.getServerView(appRoot.selectedServerId);
133+
expect(view.selectedPage).toEqual('progressView');
132134
});
133135
});
134136

src/server_manager/web_app/app.ts

+34-33
Original file line numberDiff line numberDiff line change
@@ -699,9 +699,9 @@ export class App {
699699
}
700700

701701
// Show the server management screen. Assumes the server is healthy.
702-
private setServerManagementView(server: server.Server): void {
702+
private async setServerManagementView(server: server.Server): Promise<void> {
703703
// Show view and initialize fields from selectedServer.
704-
const view = this.appRoot.getServerView(server.getId());
704+
const view = await this.appRoot.getServerView(server.getId());
705705
const version = server.getVersion();
706706
view.selectedPage = 'managementView';
707707
view.serverId = server.getId();
@@ -760,9 +760,9 @@ export class App {
760760
}, 0);
761761
}
762762

763-
private setServerUnreachableView(server: server.Server): void {
763+
private async setServerUnreachableView(server: server.Server): Promise<void> {
764764
// Display the unreachable server state within the server view.
765-
const serverView = this.appRoot.getServerView(server.getId());
765+
const serverView = await this.appRoot.getServerView(server.getId());
766766
serverView.selectedPage = 'unreachableView';
767767
serverView.isServerManaged = isManagedServer(server);
768768
serverView.serverName =
@@ -772,8 +772,8 @@ export class App {
772772
};
773773
}
774774

775-
private setServerProgressView(server: server.Server): void {
776-
const view = this.appRoot.getServerView(server.getId());
775+
private async setServerProgressView(server: server.Server): Promise<void> {
776+
const view = await this.appRoot.getServerView(server.getId());
777777
view.serverName = this.makeDisplayName(server);
778778
view.selectedPage = 'progressView';
779779
}
@@ -874,17 +874,18 @@ export class App {
874874
};
875875
}
876876

877-
private addAccessKey() {
878-
this.selectedServer.addAccessKey()
879-
.then((serverAccessKey: server.AccessKey) => {
880-
const uiAccessKey = this.convertToUiAccessKey(serverAccessKey);
881-
this.appRoot.getServerView(this.appRoot.selectedServerId).addAccessKey(uiAccessKey);
882-
this.appRoot.showNotification(this.appRoot.localize('notification-key-added'));
883-
})
884-
.catch((error) => {
885-
console.error(`Failed to add access key: ${error}`);
886-
this.appRoot.showError(this.appRoot.localize('error-key-add'));
887-
});
877+
private async addAccessKey() {
878+
const server = this.selectedServer;
879+
try {
880+
const serverAccessKey = await server.addAccessKey();
881+
const uiAccessKey = this.convertToUiAccessKey(serverAccessKey);
882+
const serverView = await this.appRoot.getServerView(server.getId());
883+
serverView.addAccessKey(uiAccessKey);
884+
this.appRoot.showNotification(this.appRoot.localize('notification-key-added'));
885+
} catch (error) {
886+
console.error(`Failed to add access key: ${error}`);
887+
this.appRoot.showError(this.appRoot.localize('error-key-add'));
888+
}
888889
}
889890

890891
private renameAccessKey(accessKeyId: string, newName: string, entry: polymer.Base) {
@@ -907,7 +908,7 @@ export class App {
907908
if (previousLimit && limit.bytes === previousLimit.bytes) {
908909
return;
909910
}
910-
const serverView = this.appRoot.getServerView(this.appRoot.selectedServerId);
911+
const serverView = await this.appRoot.getServerView(this.appRoot.selectedServerId);
911912
try {
912913
await this.selectedServer.setDefaultDataLimit(limit);
913914
this.appRoot.showNotification(this.appRoot.localize('saved'));
@@ -927,7 +928,7 @@ export class App {
927928
}
928929

929930
private async removeDefaultDataLimit() {
930-
const serverView = this.appRoot.getServerView(this.appRoot.selectedServerId);
931+
const serverView = await this.appRoot.getServerView(this.appRoot.selectedServerId);
931932
const previousLimit = this.selectedServer.getDefaultDataLimit();
932933
try {
933934
await this.selectedServer.removeDefaultDataLimit();
@@ -960,7 +961,7 @@ export class App {
960961
Promise<boolean> {
961962
this.appRoot.showNotification(this.appRoot.localize('saving'));
962963
const server = this.idServerMap.get(serverId);
963-
const serverView = this.appRoot.getServerView(server.getId());
964+
const serverView = await this.appRoot.getServerView(server.getId());
964965
try {
965966
await server.setAccessKeyDataLimit(keyId, {bytes: dataLimitBytes});
966967
this.refreshTransferStats(server, serverView);
@@ -976,7 +977,7 @@ export class App {
976977
private async removePerKeyDataLimit(serverId: string, keyId: string): Promise<boolean> {
977978
this.appRoot.showNotification(this.appRoot.localize('saving'));
978979
const server = this.idServerMap.get(serverId);
979-
const serverView = this.appRoot.getServerView(server.getId());
980+
const serverView = await this.appRoot.getServerView(server.getId());
980981
try {
981982
await server.removeAccessKeyDataLimit(keyId);
982983
this.refreshTransferStats(server, serverView);
@@ -1060,16 +1061,16 @@ export class App {
10601061
}
10611062
}
10621063

1063-
private removeAccessKey(accessKeyId: string) {
1064-
this.selectedServer.removeAccessKey(accessKeyId)
1065-
.then(() => {
1066-
this.appRoot.getServerView(this.appRoot.selectedServerId).removeAccessKey(accessKeyId);
1067-
this.appRoot.showNotification(this.appRoot.localize('notification-key-removed'));
1068-
})
1069-
.catch((error) => {
1070-
console.error(`Failed to remove access key: ${error}`);
1071-
this.appRoot.showError(this.appRoot.localize('error-key-remove'));
1072-
});
1064+
private async removeAccessKey(accessKeyId: string) {
1065+
const server = this.selectedServer;
1066+
try {
1067+
await server.removeAccessKey(accessKeyId);
1068+
(await this.appRoot.getServerView(server.getId())).removeAccessKey(accessKeyId);
1069+
this.appRoot.showNotification(this.appRoot.localize('notification-key-removed'));
1070+
} catch (error) {
1071+
console.error(`Failed to remove access key: ${error}`);
1072+
this.appRoot.showError(this.appRoot.localize('error-key-remove'));
1073+
}
10731074
}
10741075

10751076
private deleteServer(serverId: string) {
@@ -1124,7 +1125,7 @@ export class App {
11241125
}
11251126

11261127
private async setMetricsEnabled(metricsEnabled: boolean) {
1127-
const serverView = this.appRoot.getServerView(this.appRoot.selectedServerId);
1128+
const serverView = await this.appRoot.getServerView(this.appRoot.selectedServerId);
11281129
try {
11291130
await this.selectedServer.setMetricsEnabled(metricsEnabled);
11301131
this.appRoot.showNotification(this.appRoot.localize('saved'));
@@ -1140,7 +1141,7 @@ export class App {
11401141
private async renameServer(newName: string) {
11411142
const serverToRename = this.selectedServer;
11421143
const serverId = this.appRoot.selectedServerId;
1143-
const view = this.appRoot.getServerView(serverId);
1144+
const view = await this.appRoot.getServerView(serverId);
11441145
try {
11451146
await serverToRename.setName(newName);
11461147
view.serverName = newName;

src/server_manager/web_app/ui_components/app-root.js

+5-20
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import './outline-language-picker.js';
3838
import './outline-manual-server-entry.js';
3939
import './outline-modal-dialog.js';
4040
import './outline-region-picker-step';
41+
import './outline-server-list';
4142
import './outline-tos-view.js';
4243

4344
import {AppLocalizeBehavior} from '@polymer/app-localize-behavior/app-localize-behavior.js';
@@ -386,10 +387,7 @@ export class AppRoot extends mixinBehaviors
386387
<outline-gcp-oauth-step id="gcpOauth" localize="[[localize]]"></outline-gcp-oauth-step>
387388
<outline-manual-server-entry id="manualEntry" localize="[[localize]]"></outline-manual-server-entry>
388389
<outline-region-picker-step id="regionPicker" localize="[[localize]]"></outline-region-picker-step>
389-
<div id="serverView">
390-
<template is="dom-repeat" items="{{serverList}}" as="server">
391-
<outline-server-view id="serverView-{{_base64Encode(server.id)}}" language="[[language]]" localize="[[localize]]" hidden\$="{{!_isServerSelected(selectedServerId, server)}}"></outline-server-view>
392-
</template>
390+
<outline-server-list id="serverView" server-list="[[serverList]]" selected-server-id="[[selectedServerId]]" language="[[language]]" localize="[[localize]]"></outline-server-list>
393391
</div>
394392
</iron-pages>
395393
</div>
@@ -658,16 +656,10 @@ export class AppRoot extends mixinBehaviors
658656
/**
659657
* Gets the ServerView for the server given by its id
660658
* @param {string} displayServerId
661-
* @returns {ServerView}
659+
* @returns {Promise<ServerView>}
662660
*/
663-
getServerView(displayServerId) {
664-
if (!displayServerId) {
665-
return null;
666-
}
667-
// Render to ensure that the server view has been added to the DOM.
668-
this.$.serverView.querySelector('dom-repeat').render();
669-
const selectedServerId = this._base64Encode(displayServerId);
670-
return this.$.serverView.querySelector(`#serverView-${selectedServerId}`);
661+
async getServerView(displayServerId) {
662+
return await this.shadowRoot.querySelector('#serverView').getServerView(displayServerId);
671663
}
672664

673665
handleRegionSelected(/** @type {Event} */ e) {
@@ -950,12 +942,5 @@ export class AppRoot extends mixinBehaviors
950942
this.fire('ShowServerRequested', {displayServerId: server.id});
951943
this.maybeCloseDrawer();
952944
}
953-
954-
// Wrapper to encode a string in base64. This is necessary to set the server view IDs to
955-
// the display server IDs, which are URLs, so they can be used with selector methods. The IDs
956-
// are never decoded.
957-
_base64Encode(s) {
958-
return btoa(s).replace(/=/g, '');
959-
}
960945
}
961946
customElements.define(AppRoot.is, AppRoot);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2021 The Outline Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {customElement, html, LitElement, property} from 'lit-element';
16+
import {repeat} from 'lit-html/directives/repeat.js';
17+
import {ServerView} from './outline-server-view.js';
18+
19+
export interface ServerListEntry {
20+
id: string;
21+
name: string;
22+
isManaged: boolean;
23+
isSynced: boolean;
24+
}
25+
26+
@customElement('outline-server-list')
27+
export class OutlineServerList extends LitElement {
28+
@property({type: Array}) serverList: ServerListEntry[];
29+
@property({type: String}) selectedServerId: string;
30+
@property({type: Function}) localize: Function;
31+
@property({type: String}) language: string;
32+
33+
render() {
34+
if (!this.serverList) {
35+
return;
36+
}
37+
return html`<div>${repeat(this.serverList, e => e.id, e => html`
38+
<outline-server-view
39+
.id="${this.makeViewId(e.id)}"
40+
.language="${this.language}"
41+
.localize="${this.localize}"
42+
?hidden="${e.id !== this.selectedServerId}">
43+
</outline-server-view>
44+
`)}</div>`;
45+
}
46+
47+
async getServerView(serverId: string): Promise<ServerView> {
48+
if (!serverId) {
49+
return null;
50+
}
51+
// We need to wait updates to be completed or the view may not yet be there.
52+
await this.updateComplete;
53+
const selector = `#${this.makeViewId(serverId)}`;
54+
return this.shadowRoot.querySelector<ServerView>(selector);
55+
}
56+
57+
// Wrapper to encode a string in base64. This is necessary to set the server view IDs to
58+
// the display server IDs, which are URLs, so they can be used with selector methods. The IDs
59+
// are never decoded.
60+
private makeViewId(serverId: string): string {
61+
return `serverView-${btoa(serverId).replace(/=/g, '')}`;
62+
}
63+
}

src/server_manager/web_app/ui_components/outline-server-view.js

+16-21
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ export class ServerView extends DirMixin(PolymerElement) {
493493
<img class="digital-ocean-icon" src="images/do_white_logo.svg">
494494
</div>
495495
<div class="stats">
496-
<h3>[[managedServerUtilzationPercentage]]</h3>
496+
<h3>[[_computeManagedServerUtilzationPercentage(totalInboundBytes, monthlyOutboundTransferBytes)]]</h3>
497497
<p>/[[_formatBytesTransferred(monthlyOutboundTransferBytes, language)]]</p>
498498
</div>
499499
<p>[[localize('server-data-used')]]</p>
@@ -623,38 +623,33 @@ export class ServerView extends DirMixin(PolymerElement) {
623623
serverName: String,
624624
serverHostname: String,
625625
serverVersion: String,
626-
isHostnameEditable: {type: Boolean},
626+
isHostnameEditable: Boolean,
627627
serverManagementApiUrl: String,
628628
serverPortForNewAccessKeys: Number,
629-
isAccessKeyPortEditable: {type: Boolean},
630-
serverCreationDate: {type: Date},
629+
isAccessKeyPortEditable: Boolean,
630+
serverCreationDate: Date,
631631
serverLocation: String,
632632
defaultDataLimitBytes: Number,
633-
isDefaultDataLimitEnabled: {type: Boolean},
634-
supportsDefaultDataLimit: {type: Boolean},
635-
showFeatureMetricsDisclaimer: {type: Boolean},
633+
isDefaultDataLimitEnabled: Boolean,
634+
supportsDefaultDataLimit: Boolean,
635+
showFeatureMetricsDisclaimer: Boolean,
636636
isServerManaged: Boolean,
637637
isServerReachable: Boolean,
638638
retryDisplayingServer: Function,
639639
myConnection: Object,
640640
totalInboundBytes: Number,
641641
baselineDataTransfer: Number,
642-
accessKeyRows: {type: Array},
642+
accessKeyRows: Array,
643643
hasNonAdminAccessKeys: Boolean,
644644
metricsEnabled: Boolean,
645-
monthlyOutboundTransferBytes: {type: Number},
646-
monthlyCost: {type: Number},
647-
managedServerUtilzationPercentage: {
648-
type: Number,
649-
computed:
650-
'_computeManagedServerUtilzationPercentage(totalInboundBytes, monthlyOutboundTransferBytes)',
651-
},
652-
accessKeySortBy: {type: String},
653-
accessKeySortDirection: {type: Number},
654-
language: {type: String},
655-
localize: {type: Function, readonly: true},
656-
selectedPage: {type: String},
657-
selectedTab: {type: String},
645+
monthlyOutboundTransferBytes: Number,
646+
monthlyCost: Number,
647+
accessKeySortBy: String,
648+
accessKeySortDirection: Number,
649+
language: String,
650+
localize: Function,
651+
selectedPage: String,
652+
selectedTab: String,
658653
};
659654
}
660655

0 commit comments

Comments
 (0)