Skip to content

Commit 04adffe

Browse files
feat: add api request graphql command, UTs, NUTs
* feat: add api request graphql command, UTs, NUTs * test: add logging * refactor: move test files, local project * chore: populate force-app * chore: remove logging * chore: update --body flag to read file/stdin/value * chore: share flags/methods * chore: fix shared method, got method * chore: --api-version * chore: example/classes * chore: add .gitkeep in sample proj * test: add NUT with --body and directly passing in grapql --------- Co-authored-by: mshanemc <shane.mclaughlin@salesforce.com>
1 parent 86fac95 commit 04adffe

File tree

19 files changed

+601
-163
lines changed

19 files changed

+601
-163
lines changed

README.md

Lines changed: 88 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,7 @@
1-
**NOTE: This template for sf plugins is not yet official. Please consult with the Platform CLI team before using this template.**
2-
3-
# plugin-template-sf
4-
5-
[![NPM](https://img.shields.io/npm/v/@salesforce/plugin-template-sf.svg?label=@salesforce/plugin-template-sf)](https://www.npmjs.com/package/@salesforce/plugin-template-sf) [![Downloads/week](https://img.shields.io/npm/dw/@salesforce/plugin-template-sf.svg)](https://npmjs.org/package/@salesforce/plugin-template-sf) [![License](https://img.shields.io/badge/License-BSD%203--Clause-brightgreen.svg)](https://raw.githubusercontent.com/salesforcecli/plugin-template-sf/main/LICENSE.txt)
6-
7-
## Using the template
8-
9-
This repository provides a template for creating a plugin for the Salesforce CLI. To convert this template to a working plugin:
10-
11-
1. Please get in touch with the Platform CLI team. We want to help you develop your plugin.
12-
2. Generate your plugin:
13-
14-
```
15-
sf plugins install dev
16-
sf dev generate plugin
17-
18-
git init -b main
19-
git add . && git commit -m "chore: initial commit"
20-
```
21-
22-
3. Create your plugin's repo in the salesforcecli github org
23-
4. When you're ready, replace the contents of this README with the information you want.
24-
25-
## Learn about `sf` plugins
26-
27-
Salesforce CLI plugins are based on the [oclif plugin framework](https://oclif.io/docs/introduction). Read the [plugin developer guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_plugins.meta/sfdx_cli_plugins/cli_plugins_architecture_sf_cli.htm) to learn about Salesforce CLI plugin development.
28-
29-
This repository contains a lot of additional scripts and tools to help with general Salesforce node development and enforce coding standards. You should familiarize yourself with some of the [node developer packages](#tooling) used by Salesforce. There is also a default circleci config using the [release management orb](https://github.com/forcedotcom/npm-release-management-orb) standards.
30-
31-
Additionally, there are some additional tests that the Salesforce CLI will enforce if this plugin is ever bundled with the CLI. These test are included by default under the `posttest` script and it is required to keep these tests active in your plugin if you plan to have it bundled.
32-
33-
### Tooling
34-
35-
- [@salesforce/core](https://github.com/forcedotcom/sfdx-core)
36-
- [@salesforce/kit](https://github.com/forcedotcom/kit)
37-
- [@salesforce/sf-plugins-core](https://github.com/salesforcecli/sf-plugins-core)
38-
- [@salesforce/ts-types](https://github.com/forcedotcom/ts-types)
39-
- [@salesforce/ts-sinon](https://github.com/forcedotcom/ts-sinon)
40-
- [@salesforce/dev-config](https://github.com/forcedotcom/dev-config)
41-
- [@salesforce/dev-scripts](https://github.com/forcedotcom/dev-scripts)
42-
43-
# Everything past here is only a suggestion as to what should be in your specific plugin's description
44-
45-
This plugin is bundled with the [Salesforce CLI](https://developer.salesforce.com/tools/sfdxcli). For more information on the CLI, read the [getting started guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm).
46-
47-
We always recommend using the latest version of these commands bundled with the CLI, however, you can install a specific version or tag if needed.
48-
491
## Install
502

513
```bash
52-
sf plugins install @salesforce/plugin-template-sf@x.y.z
4+
sf plugins install @salesforce/plugin-api
535
```
546

557
## Issues
@@ -107,59 +59,119 @@ sf plugins
10759

10860
<!-- commands -->
10961

62+
- [`sf api request graphql`](#sf-api-request-graphql)
11063
- [`sf api request rest ENDPOINT`](#sf-api-request-rest-endpoint)
11164

112-
## `sf api request rest ENDPOINT`
65+
## `sf api request graphql`
11366

114-
Make an authenticated HTTP request to Salesforce REST API and print the response.
67+
Execute GraphQL statements
11568

11669
````
11770
USAGE
118-
$ sf api request rest ENDPOINT -o <value> [--flags-dir <value>] [--api-version <value>] [-i | -S Example:
119-
report.xlsx] [-X GET|POST|PUT|PATCH|HEAD|DELETE|OPTIONS|TRACE] [-H key:value...] [--body file]
120-
121-
ARGUMENTS
122-
ENDPOINT Salesforce API endpoint
71+
$ sf api request graphql -o <value> --body file [--json] [--flags-dir <value>] [-S Example: report.xlsx | -i]
12372
12473
FLAGS
125-
-H, --header=key:value... HTTP header in "key:value" format.
12674
-S, --stream-to-file=Example: report.xlsx Stream responses to a file.
127-
-X, --method=<option> [default: GET] HTTP method for the request.
128-
<options: GET|POST|PUT|PATCH|HEAD|DELETE|OPTIONS|TRACE>
12975
-i, --include Include the HTTP response status and headers in the output.
13076
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the
13177
`target-org` configuration variable is already set.
132-
--api-version=<value> Override the api version used for api requests made by this command
133-
--body=file File to use as the body for the request. Specify "-" to read from standard
134-
input; specify "" for an empty body.
78+
--body=file (required) File or content with GraphQL statement. Specify "-" to read from
79+
standard input.
13580
13681
GLOBAL FLAGS
13782
--flags-dir=<value> Import flag values from a directory.
83+
--json Format output as json.
84+
85+
DESCRIPTION
86+
Execute GraphQL statements
87+
88+
Run any valid GraphQL statement via the /graphql
89+
[API](https://developer.salesforce.com/docs/platform/graphql/guide/graphql-about.html)
13890
13991
EXAMPLES
140-
- List information about limits in the org with alias "my-org":
141-
sf api request rest 'limits' --target-org my-org
142-
- List all endpoints
143-
sf api request rest '/'
144-
- Get the response in XML format by specifying the "Accept" HTTP header:
145-
sf api request rest 'limits' --target-org my-org --header 'Accept: application/xml'
146-
- POST to create an Account object
147-
sf api request rest 'sobjects/account' --body "{\"Name\" : \"Account from REST API\",\"ShippingCity\" : \"Boise\"}" --method POST
148-
- or with a file 'info.json' containing
149-
```json
150-
{
151-
"Name": "Demo",
152-
"ShippingCity": "Boise"
92+
- Runs the graphql query directly via the command line
93+
sf api request graphql --body '{ "query": "query accounts { uiapi { query { Account { edges { node { Id \n Name { value } } } } } } }" }'
94+
- Runs a mutation to create an Account, with an `example.txt` file, containing
95+
```text
96+
mutation AccountExample{
97+
uiapi {
98+
AccountCreate(input: {
99+
Account: {
100+
Name: "Trailblazer Express"
101+
}
102+
}) {
103+
Record {
104+
Id
105+
Name {
106+
value
107+
}
108+
}
109+
}
110+
}
153111
}
154112
````
155113

114+
$ sf api request graphql --body example.txt
115+
will create a new account returning specified fields (Id, Name)
116+
117+
```
118+
119+
_See code: [src/commands/api/request/graphql.ts](https://github.com/salesforcecli/plugin-api/blob/v1.1.0/src/commands/api/request/graphql.ts)_
120+
121+
## `sf api request rest ENDPOINT`
122+
123+
Make an authenticated HTTP request to Salesforce REST API and print the response.
124+
125+
```
126+
127+
USAGE
128+
$ sf api request rest ENDPOINT -o <value> [--flags-dir <value>] [--api-version <value>] [-i | -S Example:
129+
report.xlsx] [-X GET|POST|PUT|PATCH|HEAD|DELETE|OPTIONS|TRACE] [-H key:value...] [--body file]
130+
131+
ARGUMENTS
132+
ENDPOINT Salesforce API endpoint
133+
134+
FLAGS
135+
-H, --header=key:value... HTTP header in "key:value" format.
136+
-S, --stream-to-file=Example: report.xlsx Stream responses to a file.
137+
-X, --method=<option> [default: GET] HTTP method for the request.
138+
<options: GET|POST|PUT|PATCH|HEAD|DELETE|OPTIONS|TRACE>
139+
-i, --include Include the HTTP response status and headers in the output.
140+
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the
141+
`target-org` configuration variable is already set.
142+
--api-version=<value> Override the api version used for api requests made by this command
143+
--body=file File to use as the body for the request. Specify "-" to read from standard
144+
input; specify "" for an empty body.
145+
146+
GLOBAL FLAGS
147+
--flags-dir=<value> Import flag values from a directory.
148+
149+
EXAMPLES
150+
151+
- List information about limits in the org with alias "my-org":
152+
sf api request rest 'limits' --target-org my-org
153+
- List all endpoints
154+
sf api request rest '/'
155+
- Get the response in XML format by specifying the "Accept" HTTP header:
156+
sf api request rest 'limits' --target-org my-org --header 'Accept: application/xml'
157+
- POST to create an Account object
158+
sf api request rest 'sobjects/account' --body "{\"Name\" : \"Account from REST API\",\"ShippingCity\" : \"Boise\"}" --method POST
159+
- or with a file 'info.json' containing
160+
161+
```json
162+
{
163+
"Name": "Demo",
164+
"ShippingCity": "Boise"
165+
}
166+
```
167+
156168
$ sf api request rest 'sobjects/account' --body info.json --method POST
157169

158170
- Update object
159171
sf api request rest 'sobjects/account/<Account ID>' --body "{\"BillingCity\": \"San Francisco\"}" --method PATCH
160172

161173
```
162174
163-
_See code: [src/commands/api/request/rest.ts](https://github.com/salesforcecli/plugin-api/blob/0.1.0/src/commands/api/request/rest.ts)_
175+
_See code: [src/commands/api/request/rest.ts](https://github.com/salesforcecli/plugin-api/blob/v1.1.0/src/commands/api/request/rest.ts)_
164176
<!-- commandsstop -->
165177
```

command-snapshot.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
[
2+
{
3+
"alias": [],
4+
"command": "api:request:graphql",
5+
"flagAliases": [],
6+
"flagChars": ["S", "i", "o"],
7+
"flags": ["api-version", "body", "flags-dir", "include", "json", "stream-to-file", "target-org"],
8+
"plugin": "@salesforce/plugin-api"
9+
},
210
{
311
"alias": [],
412
"command": "api:request:rest",

messages/graphql.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# summary
2+
3+
Execute GraphQL statements
4+
5+
# description
6+
7+
Run any valid GraphQL statement via the /graphql [API](https://developer.salesforce.com/docs/platform/graphql/guide/graphql-about.html)
8+
9+
# examples
10+
11+
- Runs the graphql query directly via the command line
12+
13+
<%= config.bin %> <%= command.id %> --body "query accounts { uiapi { query { Account { edges { node { Id \n Name { value } } } } } } }"
14+
15+
- Runs a mutation to create an Account, with an `example.txt` file, containing
16+
17+
```text
18+
mutation AccountExample{
19+
uiapi {
20+
AccountCreate(input: {
21+
Account: {
22+
Name: "Trailblazer Express"
23+
}
24+
}) {
25+
Record {
26+
Id
27+
Name {
28+
value
29+
}
30+
}
31+
}
32+
}
33+
}
34+
```
35+
36+
<%= config.bin %> <%= command.id %> --body example.txt
37+
38+
will create a new account returning specified fields (Id, Name)
39+
40+
# flags.header.summary
41+
42+
HTTP header in "key:value" format.
43+
44+
# flags.body.summary
45+
46+
File or content with GraphQL statement. Specify "-" to read from standard input.

messages/rest.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ Make an authenticated HTTP request to Salesforce REST API and print the response
3535

3636
<%= config.bin %> <%= command.id %> 'sobjects/account/<Account ID>' --body "{\"BillingCity\": \"San Francisco\"}" --method PATCH
3737

38-
# flags.include.summary
39-
40-
Include the HTTP response status and headers in the output.
41-
4238
# flags.method.summary
4339

4440
HTTP method for the request.
@@ -47,10 +43,6 @@ HTTP method for the request.
4743

4844
HTTP header in "key:value" format.
4945

50-
# flags.stream-to-file.summary
51-
52-
Stream responses to a file.
53-
5446
# flags.body.summary
5547

5648
File to use as the body for the request. Specify "-" to read from standard input; specify "" for an empty body.

messages/shared.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# flags.include.summary
2+
3+
Include the HTTP response status and headers in the output.
4+
5+
# flags.stream-to-file.summary
6+
7+
Stream responses to a file.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@salesforce/core": "^8.4.0",
1010
"@salesforce/kit": "^3.2.1",
1111
"@salesforce/sf-plugins-core": "^11.3.2",
12+
"@salesforce/ts-types": "^2.0.12",
1213
"ansis": "^3.3.2",
1314
"got": "^13.0.0",
1415
"proxy-agent": "^6.4.0"

src/commands/api/request/graphql.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2023, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import fs from 'node:fs';
9+
import * as os from 'node:os';
10+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
11+
import { Messages, Org, SFDX_HTTP_HEADERS } from '@salesforce/core';
12+
import { ProxyAgent } from 'proxy-agent';
13+
import { includeFlag, sendAndPrintRequest, streamToFileFlag } from '../../../shared/shared.js';
14+
15+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
16+
const messages = Messages.loadMessages('@salesforce/plugin-api', 'graphql');
17+
18+
export default class Graphql extends SfCommand<void> {
19+
public static readonly summary = messages.getMessage('summary');
20+
public static readonly description = messages.getMessage('description');
21+
public static readonly examples = messages.getMessages('examples');
22+
public static readonly state = 'beta';
23+
24+
public static readonly flags = {
25+
'target-org': Flags.requiredOrg(),
26+
'api-version': Flags.orgApiVersion(),
27+
'stream-to-file': streamToFileFlag,
28+
include: includeFlag,
29+
body: Flags.string({
30+
summary: messages.getMessage('flags.body.summary'),
31+
allowStdin: true,
32+
helpValue: 'file',
33+
required: true,
34+
}),
35+
};
36+
37+
public async run(): Promise<void> {
38+
const { flags } = await this.parse(Graphql);
39+
40+
const org = flags['target-org'];
41+
const streamFile = flags['stream-to-file'];
42+
const apiVersion = flags['api-version'] ?? (await org.retrieveMaxApiVersion());
43+
const body = `{"query":"${(fs.existsSync(flags.body) ? fs.readFileSync(flags.body, 'utf8') : flags.body)
44+
.replaceAll(os.EOL, '\\n')
45+
.replaceAll('"', '\\"')}"}`;
46+
const url = new URL(`${org.getField<string>(Org.Fields.INSTANCE_URL)}/services/data/v${apiVersion}/graphql`);
47+
48+
await org.refreshAuth();
49+
50+
const options = {
51+
agent: { https: new ProxyAgent() },
52+
headers: {
53+
...SFDX_HTTP_HEADERS,
54+
Authorization: `Bearer ${org.getConnection(apiVersion).getConnectionOptions().accessToken!}`,
55+
},
56+
body,
57+
throwHttpErrors: false,
58+
followRedirect: false,
59+
};
60+
61+
await sendAndPrintRequest({ streamFile, url, options, include: flags.include, this: this });
62+
}
63+
}

0 commit comments

Comments
 (0)