Skip to content

Commit d2c8fdf

Browse files
committed
serve/simple: Use message passing instead of HTTP
Implements a frameworks to support asynchronous requests using messages. Signed-off-by: Tyler Smalley <[email protected]>
1 parent b946afc commit d2c8fdf

File tree

12 files changed

+183
-228
lines changed

12 files changed

+183
-228
lines changed

package.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,6 @@
109109
],
110110
"menus": {
111111
"view/title": [
112-
{
113-
"command": "tailscale.refreshServe",
114-
"group": "overflow"
115-
},
116112
{
117113
"command": "tailscale.resetServe",
118114
"group": "overflow"
@@ -139,11 +135,6 @@
139135
"title": "Reset",
140136
"category": "Tailscale"
141137
},
142-
{
143-
"command": "tailscale.refreshServe",
144-
"title": "Refresh",
145-
"category": "Tailscale"
146-
},
147138
{
148139
"command": "tailscale.openFunnelPanel",
149140
"title": "Open Funnel Panel",

src/extension.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,10 @@ export async function activate(context: vscode.ExtensionContext) {
7777
tailscaleInstance
7878
);
7979

80-
context.subscriptions.push(
81-
vscode.commands.registerCommand('tailscale.refreshServe', () => {
82-
Logger.info('called tailscale.refreshServe', 'command');
83-
servePanelProvider.refreshState();
84-
})
85-
);
86-
8780
context.subscriptions.push(
8881
vscode.commands.registerCommand('tailscale.resetServe', async () => {
8982
Logger.info('called tailscale.resetServe', 'command');
9083
await tailscaleInstance.serveDelete();
91-
servePanelProvider.refreshState();
92-
9384
vscode.window.showInformationMessage('Serve configuration reset');
9485
})
9586
);

src/serve-panel-provider.ts

Lines changed: 21 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@ export class ServePanelProvider implements vscode.WebviewViewProvider {
1818
this._view.webview.postMessage(message);
1919
}
2020

21-
public async refreshState() {
22-
this.postMessage({
23-
type: 'refreshState',
24-
});
25-
}
26-
2721
resolveWebviewView(webviewView: vscode.WebviewView) {
2822
this._view = webviewView;
2923
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
@@ -33,69 +27,36 @@ export class ServePanelProvider implements vscode.WebviewViewProvider {
3327
};
3428

3529
webviewView.webview.onDidReceiveMessage(async (m: Message) => {
36-
switch (m.type) {
37-
case 'refreshState': {
38-
Logger.info('Called refreshState', 'serve-panel');
39-
await this.refreshState();
40-
break;
41-
}
42-
43-
case 'deleteServe': {
44-
Logger.info('Called deleteServe', 'serve-panel');
45-
try {
46-
await this.ts.serveDelete(m.params);
47-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
48-
} catch (e: any) {
49-
vscode.window.showErrorMessage('Unable to delete serve', e.message);
50-
}
51-
52-
await this.refreshState();
53-
break;
54-
}
55-
56-
case 'addServe': {
57-
Logger.info('Called addServe', 'serve-panel');
58-
await this.ts.serveAdd(m.params);
59-
await this.refreshState();
60-
break;
61-
}
62-
63-
case 'setFunnel': {
64-
Logger.info('Called setFunnel', 'serve-panel');
65-
try {
66-
await this.ts.setFunnel(parseInt(m.params.port), m.params.allow);
67-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
68-
} catch (e: any) {
69-
vscode.window.showErrorMessage('Unable to toggle funnel', e.message);
70-
}
71-
72-
await this.refreshState();
73-
break;
74-
}
75-
76-
case 'resetServe': {
77-
Logger.info('Called resetServe', 'serve-panel');
78-
try {
79-
await this.ts.serveDelete();
80-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
81-
} catch (e: any) {
82-
vscode.window.showErrorMessage('Unable to delete serve', e.message);
83-
}
84-
85-
await this.refreshState();
30+
const { type, data } = m;
31+
Logger.info(`called ${m.type}`, 'serve-panel');
32+
33+
let response;
34+
let error;
35+
36+
switch (type) {
37+
case 'relayRequest': {
38+
const { id, endpoint, method } = m;
39+
Logger.info(`${id}, ${endpoint}, ${method}`, 'serve-panel');
40+
response = await this.ts.performFetch(endpoint, method, data);
41+
Logger.info(`response: ${JSON.stringify(response)}`, 'serve-panel');
42+
this.postMessage({
43+
id,
44+
endpoint,
45+
method,
46+
type: 'relayResponse',
47+
data: response,
48+
});
8649
break;
8750
}
8851

8952
case 'writeToClipboard': {
90-
Logger.info('Called writeToClipboard', 'serve-panel');
91-
vscode.env.clipboard.writeText(m.params.text);
53+
vscode.env.clipboard.writeText(m.data.text);
9254
vscode.window.showInformationMessage('Copied to clipboard');
9355
break;
9456
}
9557

9658
case 'openLink': {
97-
Logger.info(`Called openLink: ${m.params.url}`, 'serve-panel');
98-
vscode.env.openExternal(vscode.Uri.parse(m.params.url));
59+
vscode.env.openExternal(vscode.Uri.parse(m.data.url));
9960
break;
10061
}
10162

src/tailscale/cli.ts

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -139,83 +139,49 @@ export class Tailscale {
139139
}
140140

141141
async serveStatus(): Promise<ServeStatus> {
142-
if (!this.url) {
143-
throw new Error('uninitialized client');
144-
}
145-
try {
146-
const resp = await fetch(`${this.url}/serve`, {
147-
headers: {
148-
Authorization: 'Basic ' + this.authkey,
149-
},
150-
});
151-
152-
const status = (await resp.json()) as ServeStatus;
153-
return status;
154-
} catch (e) {
155-
Logger.error(`error calling status: ${JSON.stringify(e, null, 2)}`);
156-
throw e;
157-
}
142+
return (await this.performFetch('/serve')) as ServeStatus;
158143
}
159144

160145
async serveAdd(p: ServeParams) {
161-
if (!this.url) {
162-
throw new Error('uninitialized client');
163-
}
164-
try {
165-
const resp = await fetch(`${this.url}/serve`, {
166-
method: 'POST',
167-
headers: {
168-
Authorization: 'Basic ' + this.authkey,
169-
},
170-
body: JSON.stringify(p),
171-
});
172-
if (!resp.ok) {
173-
throw new Error('/serve failed');
174-
}
175-
} catch (e) {
176-
Logger.info(`error adding serve: ${e}`);
177-
throw e;
178-
}
146+
await this.performFetch('/serve', 'POST', p);
179147
}
180148

181149
async serveDelete(p?: ServeParams) {
182-
if (!this.url) {
183-
throw new Error('uninitialized client');
184-
}
185-
try {
186-
const resp = await fetch(`${this.url}/serve`, {
187-
method: 'DELETE',
188-
headers: {
189-
Authorization: 'Basic ' + this.authkey,
190-
},
191-
body: JSON.stringify(p),
192-
});
193-
if (!resp.ok) {
194-
throw new Error('/serve failed');
195-
}
196-
} catch (e) {
197-
Logger.info(`error deleting serve: ${e}`);
198-
throw e;
199-
}
150+
await this.performFetch('/serve', 'DELETE', p);
200151
}
201152

202153
async setFunnel(port: number, on: boolean) {
154+
await this.performFetch('/funnel', 'POST', { port, on });
155+
}
156+
157+
async performFetch(endpoint: string, method = 'GET', body?: unknown) {
203158
if (!this.url) {
204159
throw new Error('uninitialized client');
205160
}
161+
206162
try {
207-
const resp = await fetch(`${this.url}/funnel`, {
208-
method: 'POST',
163+
const resp = await fetch(`${this.url}${endpoint}`, {
164+
method,
209165
headers: {
210166
Authorization: 'Basic ' + this.authkey,
211167
},
212-
body: JSON.stringify({ port, on }),
168+
body: body !== undefined && typeof body === 'object' ? JSON.stringify(body) : undefined,
213169
});
170+
214171
if (!resp.ok) {
215-
throw new Error('/serve failed');
172+
throw new Error(`${endpoint} failed`);
216173
}
174+
175+
const text = await resp.text();
176+
177+
try {
178+
return JSON.parse(text);
179+
// eslint-disable-next-line no-empty
180+
} catch {}
181+
182+
return text;
217183
} catch (e) {
218-
Logger.info(`error deleting serve: ${e}`);
184+
Logger.info(`error in ${method} ${endpoint}: ${e}`);
219185
throw e;
220186
}
221187
}

src/types.ts

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -71,66 +71,62 @@ export interface Version {
7171
* Messages sent from the webview to the extension.
7272
*/
7373

74-
interface RefreshState {
75-
type: 'refreshState';
74+
interface RequestBase {
75+
id?: number;
76+
type: string;
77+
data?: unknown;
7678
}
7779

78-
interface DeleteServe {
79-
type: 'deleteServe';
80-
params: ServeParams;
80+
interface RelayRequestBase extends RequestBase {
81+
type: 'relayRequest';
82+
endpoint: string;
83+
method: string;
8184
}
8285

83-
interface AddServe {
84-
type: 'addServe';
85-
params: ServeParams;
86+
interface RelayServeRequest extends RelayRequestBase {
87+
endpoint: '/serve';
88+
method: 'GET' | 'POST' | 'DELETE';
8689
}
8790

88-
interface ResetServe {
89-
type: 'resetServe';
90-
}
91-
92-
interface SetFunnel {
93-
type: 'setFunnel';
94-
params: {
95-
port: string;
96-
allow: boolean;
97-
};
98-
}
99-
100-
interface WriteToClipboard {
91+
interface WriteToClipboard extends RequestBase {
10192
type: 'writeToClipboard';
102-
params: {
93+
data: {
10394
text: string;
10495
};
10596
}
10697

107-
interface OpenLink {
98+
interface OpenLink extends RequestBase {
10899
type: 'openLink';
109-
params: {
100+
data: {
110101
url: string;
111102
};
112103
}
113104

114-
export type Message =
115-
| RefreshState
116-
| DeleteServe
117-
| AddServe
118-
| ResetServe
119-
| SetFunnel
120-
| WriteToClipboard
121-
| OpenLink;
105+
export type Message = RelayServeRequest | WriteToClipboard | OpenLink;
106+
export type MessageWithId = Omit<Message, 'id'> & { id: number };
122107

123108
/**
124109
* Messages sent from the extension to the webview.
125110
*/
126111

127-
interface UpdateState {
128-
type: 'updateState';
129-
state: ServeConfig;
112+
interface ResponseBase {
113+
id?: number;
114+
type: string;
115+
data?: unknown;
116+
error?: string;
117+
}
118+
119+
interface RelayResponseBase extends Omit<RelayRequestBase, 'type'>, Omit<ResponseBase, 'type'> {
120+
id?: number;
121+
endpoint: string;
122+
method: string;
123+
body?: unknown;
124+
error?: string;
130125
}
131126

132-
interface RefreshState {
133-
type: 'refreshState';
127+
export interface RelayServeResponse extends RelayResponseBase {
128+
type: 'relayResponse';
129+
body?: ServeStatus;
134130
}
135131

136132
interface WebpackOk {
@@ -145,7 +141,8 @@ interface WebpackStillOk {
145141
type: 'webpackStillOk';
146142
}
147143

148-
export type WebviewData = UpdateState | RefreshState | WebpackOk | WebpackInvalid | WebpackStillOk;
144+
export type Responses = RelayServeResponse;
145+
export type WebviewData = Responses | WebpackOk | WebpackInvalid | WebpackStillOk;
149146
export type WebviewEvent = Event & { data: WebviewData };
150147

151148
export interface NewPortNotification {

src/vscode-api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class VSCodeWrapper {
1111
public writeToClipboard(text: string): void {
1212
this.postMessage({
1313
type: 'writeToClipboard',
14-
params: {
14+
data: {
1515
text,
1616
},
1717
});
@@ -20,7 +20,7 @@ class VSCodeWrapper {
2020
public openLink(url: string): void {
2121
this.postMessage({
2222
type: 'openLink',
23-
params: {
23+
data: {
2424
url,
2525
},
2626
});

0 commit comments

Comments
 (0)