diff --git a/documentation/getting-started.md b/documentation/getting-started.md
index 77f8ee3..75e0fe7 100644
--- a/documentation/getting-started.md
+++ b/documentation/getting-started.md
@@ -40,6 +40,20 @@ When starting a new project or adding glide to an existing project, you may exec
glide init https://my-salesforce-instance.salesforce.com
```
+### Sandbox Setup
+
+If you wish to use glide with a sandbox environment, you may generate an additional JSON configuration file by running the `init` command again with the `--sandbox` option. When `--sandbox` is passed to the `init` command, a boolean `"sandbox": true` property is added to the resulting JSON configuration file. This option instructs the authentication layer of glide to use the correct login url (https://test.salesforce.com).
+
+```sh
+glide init https://my-salesforce-sandbox.salesforce.com --sandbox
+```
+
+In order to prevent name collisions with the default JSON configuration file generated from the `init` command, `glide.sandbox.json` will be used as the file name if the optional `path` argument is not provided. If you wish to use another file name or path, you may do so by providing the `path` argument.
+
+```sh
+glide init https://my-salesforce-sandbox.salesforce.com sandbox.json --sandbox
+```
+
**Note:**
If you have not used Glide with your Salesforce instance before, you may be prompted to login. More information about how authentication works in Glide can be found in the [_Advanced Topics_](./advanced-topics.md) section of the guides.
@@ -48,6 +62,16 @@ If you have not used Glide with your Salesforce instance before, you may be prom
Once you have a JSON configuration file in the root directory of your project, you may spawn a GraphQL server by executing the `serve` command. When executing the `serve` command in a development environment, your default browser will open a [GraphQL Playground](https://github.com/prisma/graphql-playground) playground page to start exploring your data.
+```sh
+glide serve
+```
+
+To use a different JSON configuration file then the default `glide.json` generated by the `init` command, you may do so by providing a the path to the configuration file as a positional argument to the `serve` command.
+
+```sh
+glide serve glide.sandbox.json
+```
+
You manually edit your JSON configuration file if you wish to modify the mutations, queries, and/or data types exposed by your GraphQL server. Information about how to configure the generated GraphQL schema can be found in the [_Advanced Topics_](./advanced-topics.md) section of the guides.
diff --git a/package.json b/package.json
index ebdc92a..72482d8 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@glide/runtime": "^0.0.1",
"@zakgolba/jsonptr": "^1.1.0",
"body-parser": "^1.19.0",
+ "boxen": "^4.1.0",
"chalk": "^2.4.2",
"commander": "^3.0.1",
"cross-fetch": "^3.0.4",
diff --git a/packages/glide-authenticate/package.json b/packages/glide-authenticate/package.json
index 1f8e450..e8a902b 100644
--- a/packages/glide-authenticate/package.json
+++ b/packages/glide-authenticate/package.json
@@ -5,17 +5,20 @@
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
"scripts": {
+ "deploy": "serverless deploy",
"lint": "tslint --project tsconfig.json"
},
"dependencies": {
"aws-sdk": "^2.534.0",
"jsforce": "^1.9.2",
+ "lodash": "^4.17.15",
"nconf": "^0.10.0"
},
"devDependencies": {
"@types/aws-lambda": "8.10.32",
"@types/aws-sdk": "2.7.0",
"@types/jsforce": "1.9.9",
+ "@types/lodash": "4.14.139",
"@types/nconf": "0.10.0",
"@types/node": "12.7.5",
"serverless": "^1.52.2"
diff --git a/packages/glide-authenticate/serverless.yml b/packages/glide-authenticate/serverless.yml
index 8d633ea..234bde4 100644
--- a/packages/glide-authenticate/serverless.yml
+++ b/packages/glide-authenticate/serverless.yml
@@ -1,4 +1,4 @@
-service: glide-authenticate
+service: glide-oauth
provider:
name: aws
runtime: nodejs8.10
@@ -7,23 +7,23 @@ package:
- dist/**
functions:
callback:
- handler: dist/lib.callback
+ handler: dist/events/oauth/callback.default
events:
- http:
path: oauth/callback
method: get
connect:
- handler: dist/lib.connect
+ handler: dist/events/connect.default
events:
- websocket:
route: $connect
message:
- handler: dist/lib.message
+ handler: dist/events/message.default
events:
- websocket:
route: $default
refresh:
- handler: dist/lib.refresh
+ handler: dist/events/oauth/refresh.default
events:
- http:
path: oauth/refresh
diff --git a/packages/glide-authenticate/src/events/connect.ts b/packages/glide-authenticate/src/events/connect.ts
new file mode 100644
index 0000000..4b1b433
--- /dev/null
+++ b/packages/glide-authenticate/src/events/connect.ts
@@ -0,0 +1,5 @@
+import { handler } from "../utilities";
+
+export default handler(async () => {
+ // noop
+});
diff --git a/packages/glide-authenticate/src/events/message.ts b/packages/glide-authenticate/src/events/message.ts
new file mode 100644
index 0000000..49b0f4c
--- /dev/null
+++ b/packages/glide-authenticate/src/events/message.ts
@@ -0,0 +1,17 @@
+import { get } from "lodash";
+
+import { MessageType } from "../types";
+import { handler, oauth2, send } from "../utilities";
+
+export default handler(async request => {
+ const { connectionId, domainName, stage } = request.requestContext;
+ const environment = get(JSON.parse(request.body || "{}"), "data.environment");
+ const result = oauth2.configure(environment).getAuthorizationUrl({
+ state: JSON.stringify({ connectionId, domainName, stage }),
+ });
+
+ await send(request.requestContext, {
+ data: result,
+ type: MessageType.Initialize,
+ });
+});
diff --git a/packages/glide-authenticate/src/events/oauth/callback.ts b/packages/glide-authenticate/src/events/oauth/callback.ts
new file mode 100644
index 0000000..c16f441
--- /dev/null
+++ b/packages/glide-authenticate/src/events/oauth/callback.ts
@@ -0,0 +1,26 @@
+import { Connection } from "jsforce";
+
+import { MessageType } from "../../types";
+import { handler, params, oauth2, send } from "../../utilities";
+
+export default handler(async request => {
+ const environment = params.get(request, "environment");
+ const connection = new Connection({
+ oauth2: oauth2.configure(environment),
+ });
+
+ await connection.authorize(params.require(request, "code"));
+ await send(JSON.parse(params.require(request, "state")), {
+ data: {
+ accessToken: connection.accessToken,
+ oauth2: oauth2.options(environment),
+ // @ts-ignore
+ refreshToken: connection.refreshToken,
+ },
+ type: MessageType.Authenticated,
+ });
+
+ return {
+ message: "You have been successfully logged in",
+ };
+});
diff --git a/packages/glide-authenticate/src/events/oauth/refresh.ts b/packages/glide-authenticate/src/events/oauth/refresh.ts
new file mode 100644
index 0000000..910e835
--- /dev/null
+++ b/packages/glide-authenticate/src/events/oauth/refresh.ts
@@ -0,0 +1,12 @@
+import { handler, oauth2 } from "../../utilities";
+
+export default handler(async request => {
+ const { environment, token } = JSON.parse(request.body || "{}");
+ const result = await oauth2.configure(environment).refreshToken(token);
+
+ return {
+ oauth2: oauth2.options(environment),
+ accessToken: result.access_token,
+ refreshToken: result.refresh_token,
+ };
+});
diff --git a/packages/glide-authenticate/src/lib.ts b/packages/glide-authenticate/src/lib.ts
index cb1bbdf..e69de29 100644
--- a/packages/glide-authenticate/src/lib.ts
+++ b/packages/glide-authenticate/src/lib.ts
@@ -1,60 +0,0 @@
-import { Connection, OAuth2 } from "jsforce";
-import nconf from "nconf";
-
-import { handler, params, send } from "./utilities";
-
-const oauth2 = new OAuth2(nconf.env("__").get("oauth"));
-
-const enum MessageType {
- Authenticated = "AUTHENTICATED",
- Initialize = "INITIALIZE",
-}
-
-export const callback = handler(async request => {
- const client = JSON.parse(params.require(request, "state"));
- const connection = new Connection({ oauth2 });
-
- await connection.authorize(params.require(request, "code"));
-
- await send(client, {
- data: {
- accessToken: connection.accessToken,
- oauth2: nconf.get("oauth"),
- // @ts-ignore
- refreshToken: connection.refreshToken,
- },
- type: MessageType.Authenticated,
- });
-
- return {
- message: "You have been successfully logged in",
- };
-});
-
-export const connect = handler(async () => {
- // noop
-});
-
-export const message = handler(async request => {
- const state = JSON.stringify({
- connectionId: request.requestContext.connectionId,
- domainName: request.requestContext.domainName,
- stage: request.requestContext.stage,
- });
-
- await send(request.requestContext, {
- data: oauth2.getAuthorizationUrl({ state }),
- type: MessageType.Initialize,
- });
-});
-
-export const refresh = handler(async request => {
- const { token } = JSON.parse(request.body || "{}");
- const results = await oauth2.refreshToken(token);
-
- return {
- oauth2: nconf.get("oauth"),
- accessToken: results.access_token,
- refreshToken: results.refresh_token,
- };
-});
diff --git a/packages/glide-authenticate/src/types.ts b/packages/glide-authenticate/src/types.ts
new file mode 100644
index 0000000..d48d87c
--- /dev/null
+++ b/packages/glide-authenticate/src/types.ts
@@ -0,0 +1,22 @@
+export enum Environment {
+ Default = "default",
+ Sandbox = "sandbox",
+}
+
+export namespace Environment {
+ export function from(input?: string): Environment {
+ switch (input) {
+ case Environment.Sandbox: {
+ return Environment.Sandbox;
+ }
+ default: {
+ return Environment.Default;
+ }
+ }
+ }
+}
+
+export const enum MessageType {
+ Authenticated = "AUTHENTICATED",
+ Initialize = "INITIALIZE",
+}
diff --git a/packages/glide-authenticate/src/utilities.ts b/packages/glide-authenticate/src/utilities.ts
index db67f3f..ca2c4ee 100644
--- a/packages/glide-authenticate/src/utilities.ts
+++ b/packages/glide-authenticate/src/utilities.ts
@@ -1,5 +1,9 @@
import lambda, { Context } from "aws-lambda";
import { ApiGatewayManagementApi } from "aws-sdk";
+import { OAuth2, OAuth2Options } from "jsforce";
+import nconf from "nconf";
+
+import { Environment } from "./types";
let apiGateway: ApiGatewayManagementApi | null = null;
@@ -11,6 +15,8 @@ export type Response = lambda.APIGatewayProxyResult;
export type Responder = (request: Request, context: Context) => Promise