Skip to content

Commit b69ab0e

Browse files
aerbsvc-squareup-copybara
authored andcommitted
Always request JSON response when available, and allow calling
POST endpoint with no request media type GitOrigin-RevId: 121b6b19e164efa3e8d7f69da31d4a66d00f8222
1 parent 915293c commit b69ab0e

14 files changed

+124
-118
lines changed

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

+4-41
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import { createIcon } from '@chakra-ui/icons';
3030
import { useSubmitRequest } from '@web-actions/hooks/useSubmitRequest';
3131
import { useKeyboardShortcuts } from '@web-actions/hooks/useKeyboardShortcuts';
3232
import { useAppEvent } from '@web-actions/hooks/useAppEvent';
33-
import { Select } from '@chakra-ui/react';
3433
import { APP_EVENTS } from '@web-actions/events/appEvents';
3534

3635
function App() {
@@ -51,27 +50,19 @@ function App() {
5150
submitting,
5251
response,
5352
} = useSubmitRequest(
54-
viewState.selectedCallable ?? null,
53+
viewState.selectedAction ?? null,
5554
viewState.path,
5655
viewState.headers,
5756
() => requestEditorRef.current?.editor?.getValue() ?? '',
5857
);
5958

6059
useAppEvent(APP_EVENTS.ENDPOINT_SELECTED, (selectedAction: ActionGroup) => {
61-
const callables = selectedAction.getCallablesByMethod();
62-
const defaultCallable = callables[0];
63-
64-
requestEditorRef.current?.setEndpointSelection(defaultCallable);
60+
requestEditorRef.current?.setEndpointSelection(selectedAction);
6561

6662
setViewState((curr) => ({
6763
...curr,
6864
selectedAction: selectedAction,
69-
path:
70-
defaultCallable?.pathPattern ||
71-
selectedAction.all[0]?.pathPattern ||
72-
'',
73-
selectedCallable: defaultCallable,
74-
callables: callables,
65+
path: selectedAction.path,
7566
}));
7667
});
7768

@@ -196,34 +187,6 @@ function App() {
196187
Request
197188
</Heading>
198189
<HStack flexGrow={1} w="100%">
199-
{viewState.callables.length > 0 && (
200-
<Select
201-
value={viewState.selectedCallable?.httpMethod}
202-
bg="white"
203-
width="fit-content"
204-
minWidth="fit-content"
205-
onChange={(e) => {
206-
const selected = viewState.callables.find(
207-
(it) => it.httpMethod === e.target.value,
208-
);
209-
requestEditorRef.current?.setEndpointSelection(selected);
210-
setViewState({
211-
...viewState,
212-
path: selected?.pathPattern || '',
213-
selectedCallable: selected,
214-
});
215-
}}
216-
>
217-
{viewState.callables.map((callable) => (
218-
<option
219-
key={callable.httpMethod}
220-
value={callable.httpMethod}
221-
>
222-
{callable.httpMethod}
223-
</option>
224-
))}
225-
</Select>
226-
)}
227190
<Input
228191
value={viewState.path}
229192
placeholder="Path"
@@ -235,7 +198,7 @@ function App() {
235198
});
236199
}}
237200
/>
238-
{viewState.selectedCallable && (
201+
{viewState.selectedAction?.canCall === true && (
239202
<IconButton
240203
aria-label="Run"
241204
colorScheme={'green'}

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

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import {
55
import { Header } from '@web-actions/services/ApiService';
66

77
export interface ViewState {
8-
callables: MiskWebActionDefinition[];
9-
selectedCallable?: MiskWebActionDefinition;
108
path: string;
119
selectedAction: ActionGroup | null;
1210
loading: boolean;

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

+20-24
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import {
77
import { fetchCached } from '@web-actions/network/http';
88
import MetadataClient from '@web-actions/api/MetadataClient';
99

10-
function containsJson(mediaType: string[]) {
11-
return mediaType.some((it) => it.startsWith('application/json'));
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+
);
1216
}
1317

1418
export default class RealMetadataClient implements MetadataClient {
@@ -19,43 +23,35 @@ export default class RealMetadataClient implements MetadataClient {
1923
const actionMap: Record<string, ActionGroup> = {};
2024

2125
response.all['web-actions'].metadata.forEach((it) => {
26+
it.requestType = it.requestType === 'null' ? null : it.requestType;
27+
2228
const qualifiedName = `${it.packageName}.${it.name}`;
2329
const groupKey = `${it.httpMethod} ${it.pathPattern} ${qualifiedName}`;
2430
let group = actionMap[groupKey];
31+
2532
if (group === undefined) {
2633
group = {
2734
actionName: qualifiedName,
2835
path: it.pathPattern,
2936
httpMethod: it.httpMethod || '',
30-
callables: {},
31-
getCallablesByMethod(): MiskWebActionDefinition[] {
32-
return [
33-
this.callables['POST'],
34-
this.callables['PUT'],
35-
this.callables['GET'],
36-
].filter((it) => it !== undefined);
37-
},
37+
callables: [],
3838
all: [],
39+
responseMediaTypes: [],
40+
requestMediaTypes: [],
41+
canCall: false,
42+
types: it.types,
43+
requestType: it.requestType,
3944
};
4045
actionMap[groupKey] = group;
4146
}
4247

43-
function maybeAddCallable(
44-
method: string,
45-
predicate: (it: MiskWebActionDefinition) => boolean = () => true,
46-
) {
47-
if (
48-
it.httpMethod === method &&
49-
group.callables[method] === undefined &&
50-
predicate(it)
51-
) {
52-
group.callables[method] = it;
53-
}
48+
if (it.httpMethod === 'GET' || containsJsonOrAny(it.requestMediaTypes)) {
49+
group.canCall = true;
50+
group.callables.push(it);
5451
}
5552

56-
maybeAddCallable('POST', (it) => containsJson(it.requestMediaTypes));
57-
maybeAddCallable('PUT', (it) => containsJson(it.requestMediaTypes));
58-
maybeAddCallable('GET');
53+
group.requestMediaTypes.push(...it.requestMediaTypes);
54+
group.responseMediaTypes.push(it.responseMediaType);
5955

6056
group.all.push(it);
6157
});

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

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ export interface MiskWebActionDefinition {
1010
name: string;
1111
httpMethod?: string;
1212
packageName: string;
13-
requestType: string;
13+
requestType: string | null;
1414
pathPattern: string;
1515
types: MiskObjectTypes;
1616
requestMediaTypes: string[];
17+
responseMediaType: string;
1718
}
1819

1920
export interface MiskObjectType {
@@ -31,9 +32,13 @@ export interface ActionGroup {
3132
actionName: string;
3233
path: string;
3334
httpMethod: string;
34-
callables: Record<string, MiskWebActionDefinition>;
35-
getCallablesByMethod(): MiskWebActionDefinition[];
35+
responseMediaTypes: string[];
36+
requestMediaTypes: string[];
37+
canCall: boolean;
38+
callables: MiskWebActionDefinition[];
3639
all: MiskWebActionDefinition[];
40+
types: MiskObjectTypes;
41+
requestType: string | null;
3742
}
3843

3944
export type MiskActions = Record<string, ActionGroup>;

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
ActionGroup,
23
MiskFieldDefinition,
34
MiskWebActionDefinition,
45
} from '@web-actions/api/responseTypes';
@@ -23,7 +24,7 @@ interface CompletionArgs {
2324
}
2425

2526
export default class CompletionProvider {
26-
private selection: MiskWebActionDefinition | null = null;
27+
private selection: ActionGroup | null = null;
2728

2829
private deleteTrailing(editor: Editor, curr: Position, values: string) {
2930
for (const c of values) {
@@ -177,7 +178,9 @@ export default class CompletionProvider {
177178

178179
async getCompletions(args: CompletionArgs): Promise<Completion[]> {
179180
const topLevel = parseDocument(args.text, args.cursor.index);
180-
topLevel.applyTypes(this.selection);
181+
const callables = this.selection?.callables ?? [];
182+
183+
topLevel.applyTypes(callables[0] ?? null);
181184

182185
const cursorNode = topLevel.findCursor();
183186

@@ -211,7 +214,7 @@ export default class CompletionProvider {
211214
return [];
212215
}
213216

214-
setSelection(selection: MiskWebActionDefinition | null) {
217+
setSelection(selection: ActionGroup | null) {
215218
this.selection = selection;
216219
}
217220
}

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

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

713
class CompletionTester {
814
private editor: FakeEditor;
@@ -48,10 +54,10 @@ class CompletionTester {
4854

4955
export function givenEditor(
5056
text: string,
51-
action?: MiskWebActionDefinition,
57+
action?: ActionGroup,
5258
): CompletionTester {
5359
const editor = new FakeEditor();
54-
editor.completions = providerWithAction(action || MyAction);
60+
editor.completions = providerWithAction(action || MyActionGroup);
5561
editor.setTextWithCursor(text);
5662
return new CompletionTester(editor);
5763
}

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { MiskWebActionDefinition } from '@web-actions/api/responseTypes';
1+
import {
2+
ActionGroup,
3+
MiskWebActionDefinition,
4+
} from '@web-actions/api/responseTypes';
25

36
export const MyAction: MiskWebActionDefinition = {
47
name: 'MyAction',
58
packageName: 'xyz.block',
69
requestType: 'MyActionRequest',
710
pathPattern: '/api/v1/my-action',
811
requestMediaTypes: ['application/x-protobuf'],
12+
responseMediaType: 'application/json',
913
types: {
1014
MyActionRequest: {
1115
fields: [
@@ -37,3 +41,16 @@ export const MyAction: MiskWebActionDefinition = {
3741
},
3842
},
3943
};
44+
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+
};

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

+7-16
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
import CompletionProvider from '@web-actions/completion/CompletionProvider';
22
import FakeEditor from '@web-actions/completion/__test__/FakeEditor';
3-
import { MyAction } from '@web-actions/completion/__test__/FakeMetadataClient';
3+
import { MyActionGroup } from '@web-actions/completion/__test__/FakeMetadataClient';
44
import { givenEditor } from '@web-actions/completion/__test__/CompletionTester';
5-
import { MiskWebActionDefinition } from '@web-actions/api/responseTypes';
5+
import { ActionGroup } from '@web-actions/api/responseTypes';
66

7-
export function providerWithAction(
8-
action: MiskWebActionDefinition,
9-
): CompletionProvider {
7+
export function providerWithAction(action: ActionGroup): CompletionProvider {
108
const p = new CompletionProvider();
119
p.setSelection(action);
1210
return p;
1311
}
1412

1513
test('completion from object body', async () => {
1614
const editor = new FakeEditor();
17-
editor.completions = providerWithAction(MyAction);
15+
editor.completions = providerWithAction(MyActionGroup);
1816

1917
editor.setTextWithCursor(`
2018
{
@@ -30,7 +28,7 @@ test('completion from object body', async () => {
3028

3129
test('completion from object body within field name', async () => {
3230
const editor = new FakeEditor();
33-
editor.completions = providerWithAction(MyAction);
31+
editor.completions = providerWithAction(MyActionGroup);
3432
editor.setTextWithCursor(`
3533
{
3634
"|"
@@ -45,7 +43,7 @@ test('completion from object body within field name', async () => {
4543

4644
test('completion from object body within field value', async () => {
4745
const editor = new FakeEditor();
48-
editor.completions = providerWithAction(MyAction);
46+
editor.completions = providerWithAction(MyActionGroup);
4947
editor.setTextWithCursor(`
5048
{
5149
"text": |
@@ -60,7 +58,7 @@ test('completion from object body within field value', async () => {
6058

6159
test('completion from literal in quotes', async () => {
6260
const editor = new FakeEditor();
63-
editor.completions = providerWithAction(MyAction);
61+
editor.completions = providerWithAction(MyActionGroup);
6462
editor.setTextWithCursor(`
6563
{
6664
"text": "|",
@@ -75,13 +73,6 @@ test('completion from literal in quotes', async () => {
7573

7674
test('completion from enum', async () => {
7775
const startingWith = [
78-
// `
79-
// {
80-
// "enum": "|",
81-
// }`, `
82-
// {
83-
// "enum": |,
84-
// }`,
8576
`
8677
{
8778
"enum": |

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useState } from 'react';
2-
import { MiskWebActionDefinition } from '@web-actions/api/responseTypes';
2+
import { ActionGroup } from '@web-actions/api/responseTypes';
33
import { ApiService, Header } from '@web-actions/services/ApiService';
44

55
export interface SubmitRequestState {
@@ -9,7 +9,7 @@ export interface SubmitRequestState {
99
}
1010

1111
export function useSubmitRequest(
12-
selectedCallable: MiskWebActionDefinition | null,
12+
selectedCallable: ActionGroup | null,
1313
path: string,
1414
headers: Header[],
1515
getRequestBody: () => string,

Diff for: misk-admin-web-actions/src/web-actions/parsing/ast/TopLevel.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import AstNode from '@web-actions/parsing/ast/AstNode';
22
import Unexpected from '@web-actions/parsing/ast/Unexpected';
3-
import { MiskWebActionDefinition } from '@web-actions/api/responseTypes';
3+
import {
4+
ActionGroup,
5+
MiskWebActionDefinition,
6+
} from '@web-actions/api/responseTypes';
47
import Obj from '@web-actions/parsing/ast/Obj';
58
import MiskType from '@web-actions/api/MiskType';
69

@@ -33,9 +36,9 @@ export default class TopLevel extends AstNode {
3336
);
3437
}
3538

36-
applyTypes(actionDefinition: MiskWebActionDefinition | null) {
39+
applyTypes(actionDefinition: ActionGroup | null) {
3740
if (this.obj) {
38-
if (actionDefinition) {
41+
if (actionDefinition && actionDefinition.requestType) {
3942
const type = actionDefinition.types[actionDefinition.requestType];
4043
if (type) {
4144
this.obj.applyTypes(

0 commit comments

Comments
 (0)