Skip to content

Commit 54cc8e5

Browse files
authored
Support anonymous mutations. (#806)
The problem was that `OperationDefinitionNode.name` is undefined when the mutation is anonymous. Yes undefined values were not handled correctly. This also changes usages of "executeQuery/executeMutation" in favour of a "executeGraphQL" that takes the source of a request directly.
1 parent f463149 commit 54cc8e5

File tree

3 files changed

+61
-78
lines changed

3 files changed

+61
-78
lines changed

firebase-vscode/src/firemat/code-lens-provider.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,31 @@ export class CodeLensProvider implements vscode.CodeLensProvider {
1111
): Promise<vscode.CodeLens[]> {
1212
const codeLenses: vscode.CodeLens[] = [];
1313

14+
const documentText = document.getText();
15+
1416
// TODO: replace w/ online-parser to work with malformed documents
15-
const documentNode = parse(document.getText());
17+
const documentNode = parse(documentText);
1618

1719
for (const x of documentNode.definitions) {
1820
if (x.kind === Kind.OPERATION_DEFINITION && x.loc) {
1921
const line = x.loc.startToken.line - 1;
2022
const range = new vscode.Range(line, 0, line, 0);
2123
const position = new vscode.Position(line, 0);
22-
const operationLocation = { documentPath: document.fileName, position: position };
24+
const operationLocation = {
25+
document: documentText,
26+
documentPath: document.fileName,
27+
position: position,
28+
};
2329

2430
codeLenses.push(
2531
new vscode.CodeLens(range, {
2632
title: `$(play) Execute ${x.operation as string}`,
2733
command: "firebase.firemat.executeOperation",
2834
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
29-
arguments: [x, operationLocation],
35+
arguments: [
36+
x,
37+
operationLocation,
38+
],
3039
})
3140
);
3241
}

firebase-vscode/src/firemat/execution.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function registerExecution(
4343
const item = selectedExecution.value;
4444
if (item) {
4545
broker.send("notifyFirematResults", {
46-
args: item.args ?? {},
46+
args: item.args ?? "{}",
4747
query: print(item.operation),
4848
results: item.results ?? {},
4949
displayName: item.operation.operation + ": " + item.label,
@@ -53,10 +53,14 @@ export function registerExecution(
5353

5454
async function executeOperation(
5555
ast: OperationDefinitionNode,
56-
{ documentPath, position }
56+
{
57+
document,
58+
documentPath,
59+
position,
60+
}: { documentPath: string; position: vscode.Position; document: string }
5761
) {
5862
const item = createExecution({
59-
label: ast.name.value,
63+
label: ast.name?.value ?? "anonymous",
6064
timestamp: Date.now(),
6165
state: ExecutionState.RUNNING,
6266
operation: ast,
@@ -67,19 +71,16 @@ export function registerExecution(
6771

6872
let results;
6973
try {
70-
// execute query or mutation
71-
results =
72-
ast.operation === (OPERATION_TYPE.query as string)
73-
? await firematService.executeQuery({
74-
operation_name: ast.name.value,
75-
query: print(ast),
76-
variables: executionArgsJSON.value,
77-
})
78-
: await firematService.executeMutation({
79-
operation_name: ast.name.value,
80-
mutation: print(ast),
81-
variables: executionArgsJSON.value,
82-
});
74+
// Execute queries/mutations from their source code.
75+
// That ensures that we can execute queries in unsaved files.
76+
const results = await firematService.executeGraphQL({
77+
operationName: ast.name?.value,
78+
// We send the whole unparsed document to guarantee
79+
// that there are no formatting differences between the real document
80+
// and the document that is sent to the emulator.
81+
query: document,
82+
variables: executionArgsJSON.value,
83+
});
8384

8485
// We always update the execution item, no matter what happens beforehand.
8586
} finally {

firebase-vscode/src/firemat/service.ts

Lines changed: 32 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ export class FirematService {
5050
}
5151

5252
/** Encode a body while handling the fact that "variables" is raw JSON.
53-
*
54-
* If the JSON is invalid, will throw.
55-
*/
53+
*
54+
* If the JSON is invalid, will throw.
55+
*/
5656
private _serializeBody(body: { variables?: string; [key: string]: unknown }) {
5757
if (!body.variables) {
5858
return JSON.stringify(body);
@@ -67,60 +67,6 @@ export class FirematService {
6767
});
6868
}
6969

70-
async executeMutation(params: {
71-
operation_name: String;
72-
mutation: String;
73-
variables: string;
74-
}) {
75-
// TODO: get operationSet name from firemat.yaml
76-
const body = this._serializeBody({
77-
...params,
78-
name: "projects/p/locations/l/services/local/operationSets/crud/revisions/r",
79-
});
80-
const resp = await fetch(
81-
(await this.getFirematEndpoint()) +
82-
"/v0/projects/p/locations/l/services/local/operationSets/crud/revisions/r:executeMutation",
83-
{
84-
method: "POST",
85-
headers: {
86-
Accept: "application/json",
87-
"Content-Type": "application/json",
88-
"x-mantle-admin": "all",
89-
},
90-
body,
91-
}
92-
);
93-
const result = await resp.json().catch(() => resp.text());
94-
return result;
95-
}
96-
97-
async executeQuery(params: {
98-
operation_name: String;
99-
query: String;
100-
variables: string;
101-
}) {
102-
// TODO: get operationSet name from firemat.yaml
103-
const body = this._serializeBody({
104-
...params,
105-
name: "projects/p/locations/l/services/local/operationSets/crud/revisions/r",
106-
});
107-
const resp = await fetch(
108-
(await this.getFirematEndpoint()) +
109-
"/v0/projects/p/locations/l/services/local/operationSets/crud/revisions/r:executeQuery",
110-
{
111-
method: "POST",
112-
headers: {
113-
Accept: "application/json",
114-
"Content-Type": "application/json",
115-
"x-mantle-admin": "all",
116-
},
117-
body,
118-
}
119-
);
120-
const result = await resp.json().catch(() => resp.text());
121-
return result;
122-
}
123-
12470
// This introspection is used to generate a basic graphql schema
12571
// It will not include our predefined operations, which requires a Firemat specific introspection query
12672
async introspect(): Promise<{ data?: IntrospectionQuery }> {
@@ -149,8 +95,8 @@ export class FirematService {
14995
}
15096

15197
async executeGraphQLRead(params: {
152-
query: String;
153-
operationName: String;
98+
query: string;
99+
operationName: string;
154100
variables: string;
155101
}) {
156102
try {
@@ -180,4 +126,31 @@ export class FirematService {
180126
return null;
181127
}
182128
}
129+
130+
async executeGraphQL(params: {
131+
query: string;
132+
operationName?: string;
133+
variables: string;
134+
}) {
135+
// TODO: get name programmatically
136+
const body = this._serializeBody({
137+
...params,
138+
name: "projects/p/locations/l/services/local",
139+
});
140+
const resp = await fetch(
141+
(await this.getFirematEndpoint()) +
142+
"/v0/projects/p/locations/l/services/local:executeGraphql",
143+
{
144+
method: "POST",
145+
headers: {
146+
Accept: "application/json",
147+
"Content-Type": "application/json",
148+
"x-mantle-admin": "all",
149+
},
150+
body: body,
151+
}
152+
);
153+
const result = await resp.json().catch(() => resp.text());
154+
return result;
155+
}
183156
}

0 commit comments

Comments
 (0)