-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor authentication and improve test cases
This commit refactors the authentication mechanism from using 'accessToken' to 'apiKey', reflecting these changes in relevant API calls. It also enhances various test cases by simplifying assertions, removing unnecessary attributes, and improving overall code readability. In addition, certain functions and field descriptions have been updated for clarity.
- Loading branch information
1 parent
bbe3645
commit c654b89
Showing
17 changed files
with
235 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
ACCESS_TOKEN=your_personal_access_token_here | ||
API_KEY=your_personal_access_token_here | ||
|
||
TEST_TOKEN=personal_access_token_for_tests | ||
TEST_API_KEY=personal_access_token_for_tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,81 @@ | ||
require('dotenv').config(); // Loads variables from .env file | ||
|
||
// The test call Zapier makes to ensure an access token is valid | ||
// UX TIP: Hit an endpoint that always returns data with valid credentials, | ||
// like a /profile or /me endpoint. That way the success/failure is related to | ||
// the token and not because the user didn't happen to have a recently created | ||
// record. | ||
const testAuth = (z, bundle) => { | ||
const testUrl = 'https://sutter.innovint.us/api/v1/wineries'; | ||
|
||
// Use the accessToken from bundle for authentication | ||
const accessToken = bundle.authData.accessToken || process.env.ACCESS_TOKEN; | ||
|
||
return z.request({ | ||
url: testUrl, | ||
method: 'GET', | ||
headers: { | ||
'Authorization': `Access-Token ${accessToken}`, | ||
}, | ||
}).then((response) => { | ||
if ( | ||
response.status !== 200 || | ||
response.content.includes('some_error_indicator')) { | ||
throw new Error('The provided Access Token is invalid.'); | ||
} | ||
return response; | ||
}); | ||
'use strict'; | ||
|
||
require('dotenv').config(); | ||
|
||
// You want to make a request to an endpoint that is either specifically designed | ||
// to test auth, or one that every user will have access to. eg: `/me`. | ||
// By returning the entire request object, you have access to the request and | ||
// response data for testing purposes. Your connection label can access any data | ||
// from the returned response using the `json.` prefix. eg: `{{json.username}}`. | ||
const test = (z, bundle) => | ||
z.request({url: 'https://sutter.innovint.us/api/v1/wineries'}); | ||
|
||
// This function runs after every outbound request. You can use it to check for | ||
// errors or modify the response. You can have as many as you need. They'll need | ||
// to each be registered in your index.js file. | ||
const handleBadResponses = (response, z, bundle) => { | ||
if (response.status === 401) { | ||
throw new z.errors.Error( | ||
// This message is surfaced to the user | ||
'The API Key you supplied is incorrect', | ||
'AuthenticationError', | ||
response.status, | ||
); | ||
} | ||
|
||
return response; | ||
}; | ||
|
||
const includeAccessTokenHeader = (request, z, bundle) => { | ||
// Prioritize the token from the bundle, fall back to the environment variable | ||
const accessToken = bundle.authData.accessToken || process.env.ACCESS_TOKEN; | ||
// This function runs before every outbound request. You can have as many as you | ||
// need. They'll need to each be registered in your index.js file. | ||
const includeApiKey = (request, z, bundle) => { | ||
// Use API key from bundle.authData, or fallback to the one from .env | ||
const apiKey = bundle.authData.apiKey || process.env.API_KEY; | ||
|
||
if (accessToken) { | ||
request.headers = request.headers || {}; | ||
request.headers['Authorization'] = `Access-Token ${accessToken}`; | ||
if (apiKey) { | ||
// Use these lines to include the API key in the querystring | ||
// request.params = request.params || {}; | ||
// request.params.api_key = bundle.authData.apiKey; | ||
|
||
// If you want to include the API key in the header instead, uncomment this: | ||
request.headers.Authorization = `Access-Token ${apiKey}`; | ||
} | ||
|
||
return request; | ||
}; | ||
|
||
module.exports = { | ||
type: 'custom', | ||
connectionLabel: '{{bundle.authData.connectionLabel}}', | ||
fields: [ | ||
{ | ||
key: 'accessToken', | ||
label: 'Access Token', | ||
required: true, | ||
type: 'string', | ||
helpText: 'Your personal access token for the API.', | ||
}, | ||
], | ||
test: testAuth, | ||
befores: [includeAccessTokenHeader], | ||
afters: [], | ||
config: { | ||
// "custom" is the catch-all auth type. The user supplies some info and Zapier can | ||
// make authenticated requests with it | ||
type: 'custom', | ||
|
||
// Define any input app's auth requires here. The user will be prompted to enter | ||
// this info when they connect their account. | ||
fields: [ | ||
{ | ||
key: 'apiKey', | ||
label: 'API Key', | ||
required: true, | ||
type: 'string', | ||
helpText: 'Go to the [API Details](https://cellar.innovint.us/#/developer/personal-access-token) ' + | ||
'page in your account settings to find your Personal Access Tokens for the API Key.', | ||
}, | ||
], | ||
|
||
// The test method allows Zapier to verify that the credentials a user provides | ||
// are valid. We'll execute this method whenever a user connects their account for | ||
// the first time. | ||
test, | ||
|
||
// This template string can access all the data returned from the auth test. If | ||
// you return the test object, you'll access the returned data with a label like | ||
// `{{json.X}}`. If you return `response.data` from your test, then your label can | ||
// be `{{X}}`. This can also be a function that returns a label. That function has | ||
// the standard args `(z, bundle)` and data returned from the test can be accessed | ||
// in `bundle.inputData.X`. | ||
connectionLabel: '{{json.username}}', | ||
}, | ||
befores: [includeApiKey], | ||
afters: [handleBadResponses], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,57 @@ | ||
const listWineries = (z, bundle, url) => { | ||
const options = { | ||
url: url, | ||
method: 'GET', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Authorization': `Access-Token ${bundle.authData.accessToken}`, | ||
}, | ||
}; | ||
/** | ||
* Retrieves a list of wineries for a dropdown. | ||
* @async | ||
* @param {Object} z - The 'zapier' object. | ||
* @param {Object} bundle - The bundle containing additional data. | ||
* @return {Array} - An array of wineries for the dropdown. | ||
*/ | ||
const listWineriesDropdown = async (z, bundle) => { | ||
let wineries = []; | ||
let nextPageUrl = 'https://sutter.innovint.us/api/v1/wineries'; | ||
|
||
return z.request(options) | ||
.then((response) => { | ||
response.throwForStatus(); | ||
const results = response.json; | ||
while (nextPageUrl) { | ||
const response = await z.request({ | ||
url: nextPageUrl, | ||
method: 'GET', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Authorization': `Access-Token ${bundle.authData.apiKey}`, | ||
}, | ||
}); | ||
|
||
const pageWineryIds = results.results.map( | ||
(winery) => ({id: winery.data.internalId.toString()})); | ||
response.throwForStatus(); | ||
const responseData = await response.json; | ||
|
||
// If there's a next page, recursively fetch it | ||
if (results.pagination.next) { | ||
return listWineries(z, bundle, results.pagination.next).then( | ||
(nextPageWineryIds) => pageWineryIds.concat(nextPageWineryIds)); | ||
} else { | ||
return pageWineryIds; | ||
} | ||
}); | ||
const pageWineries = responseData.results.map((winery) => ({ | ||
id: winery.data.id, | ||
name: winery.data.name, | ||
})); | ||
|
||
wineries = [...wineries, ...pageWineries]; | ||
nextPageUrl = responseData.pagination.next; | ||
} | ||
|
||
return wineries; | ||
}; | ||
|
||
module.exports = { | ||
key: 'listWineries', | ||
key: 'listWineriesDropdown', | ||
noun: 'Winery', | ||
display: { | ||
label: 'List Wineries', | ||
description: 'Returns a list of wineries.', | ||
description: 'Trigger for field dropdown of Wineries.', | ||
hidden: true, | ||
}, | ||
operation: { | ||
perform: (z, bundle) => listWineries(z, bundle, | ||
'https://sutter.innovint.us/api/v1/wineries'), | ||
inputFields: [ | ||
{ | ||
key: 'wineryId', | ||
label: 'Select a Winery', | ||
type: 'string', | ||
dynamic: 'listWineriesDropdown.id.label', | ||
}, | ||
], | ||
perform: listWineriesDropdown, | ||
canPaginate: true, | ||
}, | ||
}; |
Oops, something went wrong.