Skip to content

Commit 2d2cea0

Browse files
aerbsvc-squareup-copybara
authored andcommitted
Minor refactor to web-actions after last set of changes
GitOrigin-RevId: d9d291b132a09b659cae0b93b6cf7182f159c868
1 parent b3b11f7 commit 2d2cea0

21 files changed

+217
-196
lines changed

Diff for: misk-admin-web-actions/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"test": " npx jest src",
88
"build": "webpack",
99
"start": "webpack serve --mode development",
10-
"lint": "npx eslint src && npx prettier src --check",
10+
"lint": " tsc --noEmit && npx eslint src && npx prettier src --check",
1111
"format": "npx prettier src --write",
1212
"proxy": "cd development-proxy && npm install && npm run dev"
1313
},

Diff for: misk-admin-web-actions/src/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import EndpointSelector from '@web-actions/ui/EndpointSelection';
2323
import { ViewState } from 'src/viewState';
2424
import { fetchCached } from '@web-actions/network/http';
2525
import {
26-
ActionGroup,
26+
MiskRoute,
2727
MiskMetadataResponse,
2828
} from '@web-actions/api/responseTypes';
2929
import { createIcon } from '@chakra-ui/icons';
@@ -56,7 +56,7 @@ function App() {
5656
() => requestEditorRef.current?.editor?.getValue() ?? '',
5757
);
5858

59-
useAppEvent(APP_EVENTS.ENDPOINT_SELECTED, (selectedAction: ActionGroup) => {
59+
useAppEvent(APP_EVENTS.ENDPOINT_SELECTED, (selectedAction: MiskRoute) => {
6060
requestEditorRef.current?.setEndpointSelection(selectedAction);
6161

6262
setViewState((curr) => ({
@@ -198,7 +198,7 @@ function App() {
198198
});
199199
}}
200200
/>
201-
{viewState.selectedAction?.canCall === true && (
201+
{viewState.selectedAction?.callable === true && (
202202
<IconButton
203203
aria-label="Run"
204204
colorScheme={'green'}

Diff for: misk-admin-web-actions/src/viewState.tsx

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import {
2-
ActionGroup,
3-
MiskWebActionDefinition,
4-
} from '@web-actions/api/responseTypes';
5-
import { Header } from '@web-actions/services/ApiService';
1+
import { MiskRoute } from '@web-actions/api/responseTypes';
62

73
export interface ViewState {
84
path: string;
9-
selectedAction: ActionGroup | null;
5+
selectedAction: MiskRoute | null;
106
loading: boolean;
117
isHelpOpen: boolean;
128
headers: Header[];
139
}
10+
11+
export interface Header {
12+
key: string;
13+
value: string;
14+
}
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {
2+
MiskRoute,
3+
MiskWebActionDefinition,
4+
} from 'src/web-actions/api/responseTypes';
5+
import { MediaTypes } from '@web-actions/api/MediaTypes';
6+
import { parseNull } from '@web-actions/utils/common';
7+
8+
export function buildRoutes(actions: MiskWebActionDefinition[]): MiskRoute[] {
9+
const routeMap: Record<string, MiskRoute> = {};
10+
11+
for (const it of actions) {
12+
const qualifiedName = `${it.packageName}.${it.name}`;
13+
const groupKey = `${it.httpMethod} ${it.pathPattern} ${qualifiedName}`;
14+
let group = routeMap[groupKey];
15+
16+
if (group === undefined) {
17+
group = {
18+
actionName: qualifiedName,
19+
path: it.pathPattern,
20+
httpMethod: it.httpMethod || '',
21+
responseMediaTypes: new MediaTypes(),
22+
requestMediaTypes: new MediaTypes(),
23+
types: it.types,
24+
requestType: it.requestType,
25+
all: [],
26+
};
27+
routeMap[groupKey] = group;
28+
}
29+
group.requestMediaTypes.push(...it.requestMediaTypes);
30+
group.responseMediaTypes.push(it.responseMediaType);
31+
group.all.push({
32+
...it,
33+
requestType: parseNull(it.requestType),
34+
});
35+
}
36+
37+
const routes = Object.values(routeMap);
38+
39+
for (const group of routes) {
40+
if (group.httpMethod === 'GET') {
41+
group.callable = true;
42+
} else if (group.httpMethod === 'PUT' || group.httpMethod === 'POST') {
43+
if (
44+
group.requestMediaTypes.hasJson() ||
45+
group.requestMediaTypes.hasAny() ||
46+
group.requestMediaTypes.isUnspecified()
47+
) {
48+
group.callable = true;
49+
}
50+
}
51+
}
52+
return routes;
53+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export class MediaTypes {
2+
private readonly types: string[];
3+
4+
constructor(types: string[] = []) {
5+
this.types = types;
6+
}
7+
8+
push(...types: string[]) {
9+
this.types.push(...types);
10+
}
11+
12+
static of(types: string[]): MediaTypes {
13+
return new MediaTypes(types);
14+
}
15+
16+
hasJson(): boolean {
17+
return this.types.some((type) => type.startsWith('application/json'));
18+
}
19+
20+
hasAny(): boolean {
21+
return this.types.some((type) => type.startsWith('*/*'));
22+
}
23+
24+
isUnspecified(): boolean {
25+
return this.types.length === 0;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { MiskActions } from '@web-actions/api/responseTypes';
1+
import { MiskRoute } from '@web-actions/api/responseTypes';
22

33
export default interface MetadataClient {
4-
fetchMetadata(): Promise<MiskActions>;
4+
fetchMetadata(): Promise<MiskRoute[]>;
55
}
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,15 @@
11
import {
2-
ActionGroup,
3-
MiskActions,
42
MiskMetadataResponse,
5-
MiskWebActionDefinition,
3+
MiskRoute,
64
} from '@web-actions/api/responseTypes';
75
import { fetchCached } from '@web-actions/network/http';
86
import MetadataClient from '@web-actions/api/MetadataClient';
9-
10-
function containsJsonOrAny(mediaType: string[]) {
11-
return (
12-
mediaType.length == 0 ||
13-
mediaType.some((it) => it.startsWith('application/json')) ||
14-
mediaType.some((it) => it.startsWith('*/*'))
15-
);
16-
}
7+
import { buildRoutes } from 'src/web-actions/api/BuildRoutes';
178

189
export default class RealMetadataClient implements MetadataClient {
19-
async fetchMetadata(): Promise<MiskActions> {
20-
const response = await fetchCached<MiskMetadataResponse>(
21-
`/api/web-actions/metadata`,
10+
async fetchMetadata(): Promise<MiskRoute[]> {
11+
return fetchCached<MiskMetadataResponse>(`/api/web-actions/metadata`).then(
12+
(it) => buildRoutes(it.all['web-actions'].metadata),
2213
);
23-
const actionMap: Record<string, ActionGroup> = {};
24-
25-
response.all['web-actions'].metadata.forEach((it) => {
26-
it.requestType = it.requestType === 'null' ? null : it.requestType;
27-
28-
const qualifiedName = `${it.packageName}.${it.name}`;
29-
const groupKey = `${it.httpMethod} ${it.pathPattern} ${qualifiedName}`;
30-
let group = actionMap[groupKey];
31-
32-
if (group === undefined) {
33-
group = {
34-
actionName: qualifiedName,
35-
path: it.pathPattern,
36-
httpMethod: it.httpMethod || '',
37-
callables: [],
38-
all: [],
39-
responseMediaTypes: [],
40-
requestMediaTypes: [],
41-
canCall: false,
42-
types: it.types,
43-
requestType: it.requestType,
44-
};
45-
actionMap[groupKey] = group;
46-
}
47-
48-
if (it.httpMethod === 'GET' || containsJsonOrAny(it.requestMediaTypes)) {
49-
group.canCall = true;
50-
group.callables.push(it);
51-
}
52-
53-
group.requestMediaTypes.push(...it.requestMediaTypes);
54-
group.responseMediaTypes.push(it.responseMediaType);
55-
56-
group.all.push(it);
57-
});
58-
59-
return actionMap;
6014
}
6115
}

Diff for: misk-admin-web-actions/src/web-actions/api/responseTypes.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { MediaTypes } from 'src/web-actions/api/MediaTypes';
2+
13
export interface MiskMetadataResponse {
24
all: {
35
'web-actions': {
@@ -28,19 +30,16 @@ export interface MiskFieldDefinition {
2830
annotations: any[];
2931
}
3032

31-
export interface ActionGroup {
33+
export interface MiskRoute {
3234
actionName: string;
3335
path: string;
3436
httpMethod: string;
35-
responseMediaTypes: string[];
36-
requestMediaTypes: string[];
37-
canCall: boolean;
38-
callables: MiskWebActionDefinition[];
37+
responseMediaTypes: MediaTypes;
38+
requestMediaTypes: MediaTypes;
3939
all: MiskWebActionDefinition[];
4040
types: MiskObjectTypes;
4141
requestType: string | null;
42+
callable?: boolean;
4243
}
4344

44-
export type MiskActions = Record<string, ActionGroup>;
45-
4645
export type MiskObjectTypes = Record<string, MiskObjectType>;

Diff for: misk-admin-web-actions/src/web-actions/completion/CompletionProvider.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
ActionGroup,
3-
MiskFieldDefinition,
4-
MiskWebActionDefinition,
5-
} from '@web-actions/api/responseTypes';
1+
import { MiskRoute, MiskFieldDefinition } from '@web-actions/api/responseTypes';
62
import { parseDocument } from '@web-actions/parsing/CommandParser';
73
import Obj from '@web-actions/parsing/ast/Obj';
84
import Field from '@web-actions/parsing/ast/Field';
@@ -24,7 +20,7 @@ interface CompletionArgs {
2420
}
2521

2622
export default class CompletionProvider {
27-
private selection: ActionGroup | null = null;
23+
private selection: MiskRoute | null = null;
2824

2925
private deleteTrailing(editor: Editor, curr: Position, values: string) {
3026
for (const c of values) {
@@ -212,7 +208,7 @@ export default class CompletionProvider {
212208
return [];
213209
}
214210

215-
setSelection(selection: ActionGroup | null) {
211+
setSelection(selection: MiskRoute | null) {
216212
this.selection = selection;
217213
}
218214
}

Diff for: misk-admin-web-actions/src/web-actions/completion/__test__/CompletionTester.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
import FakeEditor from '@web-actions/completion/__test__/FakeEditor';
2-
import {
3-
MyAction,
4-
MyActionGroup,
5-
} from '@web-actions/completion/__test__/FakeMetadataClient';
2+
import { MyActionGroup } from '@web-actions/completion/__test__/FakeMetadataClient';
63
import Completion from '@web-actions/completion/Completion';
74
import { providerWithAction } from '@web-actions/completion/__test__/completion.spec';
8-
import {
9-
ActionGroup,
10-
MiskWebActionDefinition,
11-
} from '@web-actions/api/responseTypes';
5+
import { MiskRoute } from '@web-actions/api/responseTypes';
126

137
class CompletionTester {
148
private editor: FakeEditor;
@@ -54,7 +48,7 @@ class CompletionTester {
5448

5549
export function givenEditor(
5650
text: string,
57-
action?: ActionGroup,
51+
action?: MiskRoute,
5852
): CompletionTester {
5953
const editor = new FakeEditor();
6054
editor.completions = providerWithAction(action || MyActionGroup);

Diff for: misk-admin-web-actions/src/web-actions/completion/__test__/FakeMetadataClient.ts

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {
2-
ActionGroup,
2+
MiskRoute,
33
MiskWebActionDefinition,
44
} from '@web-actions/api/responseTypes';
5+
import { buildRoutes } from 'src/web-actions/api/BuildRoutes';
56

67
export const MyAction: MiskWebActionDefinition = {
78
name: 'MyAction',
@@ -42,15 +43,4 @@ export const MyAction: MiskWebActionDefinition = {
4243
},
4344
};
4445

45-
export const MyActionGroup: ActionGroup = {
46-
actionName: MyAction.name,
47-
path: MyAction.pathPattern,
48-
httpMethod: MyAction.httpMethod || '',
49-
responseMediaTypes: [MyAction.responseMediaType],
50-
requestMediaTypes: MyAction.requestMediaTypes,
51-
canCall: true,
52-
callables: [MyAction],
53-
all: [MyAction],
54-
types: MyAction.types,
55-
requestType: MyAction.requestType,
56-
};
46+
export const MyActionGroup: MiskRoute = buildRoutes([MyAction])[0];

Diff for: misk-admin-web-actions/src/web-actions/completion/__test__/completion.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import CompletionProvider from '@web-actions/completion/CompletionProvider';
22
import FakeEditor from '@web-actions/completion/__test__/FakeEditor';
33
import { MyActionGroup } from '@web-actions/completion/__test__/FakeMetadataClient';
44
import { givenEditor } from '@web-actions/completion/__test__/CompletionTester';
5-
import { ActionGroup } from '@web-actions/api/responseTypes';
5+
import { MiskRoute } from '@web-actions/api/responseTypes';
66

7-
export function providerWithAction(action: ActionGroup): CompletionProvider {
7+
export function providerWithAction(action: MiskRoute): CompletionProvider {
88
const p = new CompletionProvider();
99
p.setSelection(action);
1010
return p;

Diff for: misk-admin-web-actions/src/web-actions/hooks/useShortcutEvent.ts

-17
This file was deleted.

Diff for: misk-admin-web-actions/src/web-actions/hooks/useSubmitRequest.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { useCallback, useState } from 'react';
2-
import { ActionGroup } from '@web-actions/api/responseTypes';
3-
import { ApiService, Header } from '@web-actions/services/ApiService';
2+
import { MiskRoute } from '@web-actions/api/responseTypes';
3+
import {
4+
ApiService,
5+
JsonValidationError,
6+
} from '@web-actions/services/ApiService';
7+
import { APP_EVENTS, appEvents } from 'src/web-actions/events/appEvents';
8+
import { Header } from 'src/viewState';
49

510
export interface SubmitRequestState {
611
submit: () => Promise<void>;
@@ -9,7 +14,7 @@ export interface SubmitRequestState {
914
}
1015

1116
export function useSubmitRequest(
12-
selectedCallable: ActionGroup | null,
17+
selectedCallable: MiskRoute | null,
1318
path: string,
1419
headers: Header[],
1520
getRequestBody: () => string,
@@ -25,13 +30,17 @@ export function useSubmitRequest(
2530
setLoading(true);
2631
try {
2732
const response = await ApiService.submitRequest({
28-
action: selectedCallable,
33+
route: selectedCallable,
2934
path: path,
3035
requestBody: getRequestBody(),
3136
headers: headers,
3237
});
3338

3439
setResponse(response);
40+
} catch (e) {
41+
if (e instanceof JsonValidationError) {
42+
appEvents.emit(APP_EVENTS.SHOW_ERROR_TOAST);
43+
}
3544
} finally {
3645
setLoading(false);
3746
}

0 commit comments

Comments
 (0)