-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 4fc1a4c
Showing
17 changed files
with
1,142 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,6 @@ | ||
node_modules | ||
npm-debug.log | ||
npm-debug.log.* | ||
*.log | ||
.DS_Store | ||
.env |
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 |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Change Log | ||
|
||
All notable changes to this project will be documented in this file. | ||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||
|
||
## [4.1.5](https://github.com/HubSpot/hubspot-cli/compare/v4.1.5-beta.4...v4.1.5) (2023-01-09) | ||
|
||
**Note:** Version bump only for package @hubspot/serverless-dev-runtime | ||
|
||
|
||
|
||
|
||
|
||
## [3.0.4](https://github.com/HubSpot/hubspot-cli/compare/v3.0.4-beta.1...v3.0.4) (2021-04-01) | ||
|
||
**Note:** Version bump only for package @hubspot/serverless-dev-runtime |
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 |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# @hubspot/serverless-dev-runtime | ||
|
||
A serverless function development runtime that can be used to test CMS serverless functions. This is intended for use with the [CMS CLI](https://developers.hubspot.com/docs/cms/developer-reference/local-development-cms-cli). | ||
|
||
⚠️ **This is a BETA release that uses some HubSpot features that are not available to all customer accounts. Please refer to the HubSpot [Developer Beta Terms](https://legal.hubspot.com/developerbetaterms)** ⚠️ | ||
|
||
## Getting started | ||
|
||
For more information on using these tools, see [Local Development Tooling: Getting Started](https://designers.hubspot.com/tutorials/getting-started-with-local-development). | ||
|
||
### Usage | ||
|
||
#### CLI Command (recommended) | ||
Using the CLI to run serverless functions locally, requires installing [@hubspot/cli](https://www.npmjs.com/package/@hubspot/cms-cli). Once installed, to test your functions run… | ||
|
||
```bash | ||
hs functions test <folder.functions> | ||
``` | ||
|
||
#### Importing | ||
|
||
It also is possible to use the runtime inside your own tooling. To start the server, the `start` method can be imported from the `@hubspot/serverless-dev-runtime` package and run with settings like so... | ||
|
||
```bash | ||
const { start } = require('@hubspot/serverless-dev-runtime'); | ||
|
||
start({ | ||
accountId: <portalId/accountId>, // default: 123456 | ||
contact: <booleanValueToSpecifyIfContactDataShouldBePassedToServerlessFunction>, // default: true | ||
path: <pathToLocalDotFunctionsFolder>, // required | ||
port: <customPortToRunServerOn> // default: 5432 | ||
}); | ||
``` | ||
### Mocked Data | ||
Some of the data that is passed to the serverless function context is mocked. Specifically the `contact` and `limits` properties. It is possible | ||
to modify the mocked data by setting values for specific variables within a `.env` file within the `.functions` folder. | ||
The variables used to modify the data are: | ||
``` | ||
HUBSPOT_LIMITS_TIME_REMAINING // default: 600000 | ||
HUBSPOT_LIMITS_EXECUTIONS_REMAINING // default: 60 | ||
HUBSPOT_CONTACT_VID // default: 123 | ||
HUBSPOT_CONTACT_IS_LOGGED_IN // default: false | ||
HUBSPOT_CONTACT_LIST_MEMBERSHIPS // default: [] | ||
``` | ||
Usage example `.env`: | ||
``` | ||
HUBSPOT_LIMITS_TIME_REMAINING=1000 | ||
HUBSPOT_LIMITS_EXECUTIONS_REMAINING=2 | ||
HUBSPOT_CONTACT_VID=456 | ||
HUBSPOT_CONTACT_IS_LOGGED_IN=true | ||
HUBSPOT_CONTACT_LIST_MEMBERSHIPS="some, memberships" | ||
``` |
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 |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const { start } = require('./lib/server'); | ||
|
||
module.exports = { | ||
start, | ||
}; |
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 |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// AWS does not allow overriding these | ||
// https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html#lambda-environment-variables | ||
const AWS_RESERVED_VARS = [ | ||
'_HANDLER', | ||
'LAMBDA_TASK_ROOT', | ||
'LAMBDA_RUNTIME_DIR', | ||
'AWS_EXECUTION_ENV', | ||
'AWS_DEFAULT_REGION', | ||
'AWS_REGION', | ||
'AWS_LAMBDA_LOG_GROUP_NAME', | ||
'AWS_LAMBDA_LOG_STREAM_NAME', | ||
'AWS_LAMBDA_FUNCTION_NAME', | ||
'AWS_LAMBDA_FUNCTION_MEMORY_SIZE', | ||
'AWS_LAMBDA_FUNCTION_VERSION', | ||
'AWS_ACCESS_KEY', | ||
'AWS_ACCESS_KEY_ID', | ||
'AWS_SECRET_KEY', | ||
'AWS_SECRET_ACCESS_KEY', | ||
'AWS_SESSION_TOKEN', | ||
'TZ', | ||
]; | ||
const AWS_RESERVED_VARS_INFO_URL = | ||
'https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html#lambda-environment-variables'; | ||
const MOCK_DATA = { | ||
HUBSPOT_ACCOUNT_ID: 123456, | ||
HUBSPOT_CONTACT_IS_LOGGED_IN: true, | ||
HUBSPOT_CONTACT_LIST_MEMBERSHIPS: '', | ||
HUBSPOT_CONTACT_VID: 12345, | ||
HUBSPOT_LIMITS_TIME_REMAINING: 600000, | ||
HUBSPOT_LIMITS_EXECUTIONS_REMAINING: 60, | ||
}; | ||
const MAX_SECRETS = 50; | ||
const MAX_RUNTIME = 3000; | ||
const MAX_REQ_BODY_SIZE = '50mb'; | ||
const ROUTE_PATH_PREFIX = '_hcms/api/'; | ||
const ALLOWED_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; | ||
|
||
module.exports = { | ||
ALLOWED_METHODS, | ||
AWS_RESERVED_VARS, | ||
AWS_RESERVED_VARS_INFO_URL, | ||
MAX_REQ_BODY_SIZE, | ||
MAX_RUNTIME, | ||
MAX_SECRETS, | ||
MOCK_DATA, | ||
ROUTE_PATH_PREFIX, | ||
}; |
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 |
---|---|---|
@@ -0,0 +1,151 @@ | ||
const fs = require('fs-extra'); | ||
const path = require('path'); | ||
const { logger } = require('@hubspot/local-dev-lib/logger'); | ||
const { getCwd } = require('@hubspot/local-dev-lib/path'); | ||
const { getDotEnvData } = require('./secrets'); | ||
const { MOCK_DATA, MAX_SECRETS, ROUTE_PATH_PREFIX } = require('./constants'); | ||
|
||
const getValidatedFunctionData = functionPath => { | ||
// Allow passing serverless folder path with and without .functions extension | ||
const splitPath = functionPath.split('.'); | ||
const functionPathWithExtension = | ||
splitPath[splitPath.length - 1] === 'functions' | ||
? functionPath | ||
: `${functionPath}.functions`; | ||
|
||
const resolvedFunctionPath = path.resolve( | ||
getCwd(), | ||
functionPathWithExtension | ||
); | ||
if (!fs.existsSync(resolvedFunctionPath)) { | ||
logger.error(`The path ${functionPath} does not exist.`); | ||
return; | ||
} else { | ||
const stats = fs.lstatSync(resolvedFunctionPath); | ||
if (!stats.isDirectory()) { | ||
logger.error(`${functionPath} is not a valid functions directory.`); | ||
return; | ||
} | ||
} | ||
|
||
const { endpoints = [], environment = {}, secrets = [] } = JSON.parse( | ||
fs.readFileSync(`${resolvedFunctionPath}/serverless.json`, { | ||
encoding: 'utf-8', | ||
}) | ||
); | ||
const routes = Object.keys(endpoints); | ||
|
||
if (!routes.length) { | ||
logger.error(`No endpoints found in ${functionPath}/serverless.json.`); | ||
return; | ||
} | ||
|
||
if (secrets.length > MAX_SECRETS) { | ||
logger.warn( | ||
`This function currently exceeds the limit of ${MAX_SECRETS} secrets. See https://developers.hubspot.com/docs/cms/features/serverless-functions#know-your-limits for more info.` | ||
); | ||
} | ||
|
||
return { | ||
srcPath: resolvedFunctionPath, | ||
endpoints, | ||
environment, | ||
routes, | ||
secrets, | ||
}; | ||
}; | ||
|
||
const getHeaders = req => { | ||
const reqHeaders = req.headers; | ||
|
||
return { | ||
Accept: reqHeaders.accept, | ||
'Accept-Encoding': reqHeaders['accept-encoding'], | ||
'Accept-Language': reqHeaders['accept-language'], | ||
'Cache-Control': reqHeaders['cache-control'], | ||
Connection: reqHeaders.connection, | ||
Cookie: reqHeaders.cookie, | ||
Host: reqHeaders.host, | ||
'True-Client-IP': req.ip, // https://stackoverflow.com/a/14631683/3612910 | ||
'upgrade-insecure-requests': reqHeaders['upgrade-insecure-requests'], | ||
'User-Agent': reqHeaders['user-agent'], | ||
'X-Forwarded-For': | ||
req.headers['x-forwarded-for'] || req.connection.remoteAddress, // https://stackoverflow.com/a/14631683/3612910 | ||
}; | ||
}; | ||
|
||
// This purposefully puts each param into an array to mimic the way production | ||
// does it. This should be updated when production is fixed so params work as | ||
// expected instead of always being an array. | ||
// See https://git.hubteam.com/HubSpot/ContentServerlessFunctions/pull/228 | ||
const getRequestQueryParams = req => { | ||
const paramsObj = {}; | ||
|
||
Object.keys(req.query).forEach(param => { | ||
const currentValue = req.query[param]; | ||
const newValue = Array.isArray(currentValue) | ||
? currentValue | ||
: [currentValue]; | ||
|
||
paramsObj[param] = newValue; | ||
}); | ||
|
||
return paramsObj; | ||
}; | ||
|
||
const getFunctionDataContext = async ( | ||
req, | ||
functionPath, | ||
allowedSecrets, | ||
accountId, | ||
contact | ||
) => { | ||
const { | ||
secrets, | ||
mockData: { | ||
HUBSPOT_ACCOUNT_ID, | ||
HUBSPOT_CONTACT_IS_LOGGED_IN, | ||
HUBSPOT_CONTACT_LIST_MEMBERSHIPS, | ||
HUBSPOT_CONTACT_VID, | ||
HUBSPOT_LIMITS_TIME_REMAINING, | ||
HUBSPOT_LIMITS_EXECUTIONS_REMAINING, | ||
}, | ||
} = getDotEnvData(functionPath, allowedSecrets); | ||
const data = { | ||
secrets, | ||
params: getRequestQueryParams(req), | ||
limits: { | ||
timeRemaining: | ||
HUBSPOT_LIMITS_TIME_REMAINING || | ||
MOCK_DATA.HUBSPOT_LIMITS_TIME_REMAINING, | ||
executionsRemaining: | ||
HUBSPOT_LIMITS_EXECUTIONS_REMAINING || | ||
MOCK_DATA.HUBSPOT_LIMITS_EXECUTIONS_REMAINING, | ||
}, | ||
body: req.body, | ||
headers: getHeaders(req), | ||
method: req.method, | ||
endpoint: req.url.replace(`/${ROUTE_PATH_PREFIX}`, ''), | ||
accountId: accountId || HUBSPOT_ACCOUNT_ID || MOCK_DATA.HUBSPOT_ACCOUNT_ID, | ||
contact: | ||
contact === 'true' || contact === true | ||
? { | ||
vid: HUBSPOT_CONTACT_VID || MOCK_DATA.HUBSPOT_CONTACT_VID, | ||
isLoggedIn: | ||
HUBSPOT_CONTACT_IS_LOGGED_IN || | ||
MOCK_DATA.HUBSPOT_CONTACT_IS_LOGGED_IN, | ||
listMemberships: ( | ||
HUBSPOT_CONTACT_LIST_MEMBERSHIPS || | ||
MOCK_DATA.HUBSPOT_CONTACT_LIST_MEMBERSHIPS | ||
).split(','), | ||
} | ||
: null, | ||
}; | ||
|
||
return data; | ||
}; | ||
|
||
module.exports = { | ||
getValidatedFunctionData, | ||
getFunctionDataContext, | ||
}; |
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 |
---|---|---|
@@ -0,0 +1,42 @@ | ||
const { logger } = require('@hubspot/local-dev-lib/logger'); | ||
const { | ||
AWS_RESERVED_VARS, | ||
AWS_RESERVED_VARS_INFO_URL, | ||
} = require('./constants'); | ||
|
||
const loadEnvironmentVariables = ( | ||
globalEnvironment = {}, | ||
localEnvironment = {} | ||
) => { | ||
Object.keys(globalEnvironment).forEach(globalEnvironmentVariable => { | ||
if (AWS_RESERVED_VARS.indexOf(globalEnvironmentVariable) !== -1) { | ||
logger.warn( | ||
`The variable ${globalEnvironmentVariable} is a reserved AWS variable and should not be used. See ${AWS_RESERVED_VARS_INFO_URL} for more info.` | ||
); | ||
} | ||
|
||
logger.debug( | ||
`Setting environment variable(global) ${globalEnvironmentVariable} to ${localEnvironment[globalEnvironmentVariable]}.` | ||
); | ||
process.env[globalEnvironmentVariable] = | ||
globalEnvironment[globalEnvironmentVariable]; | ||
}); | ||
|
||
Object.keys(localEnvironment).forEach(localEnvironmentVariable => { | ||
if (AWS_RESERVED_VARS.indexOf(localEnvironmentVariable) !== -1) { | ||
logger.warn( | ||
`The variable ${localEnvironmentVariable} is a reserved AWS variable and should not be used. See ${AWS_RESERVED_VARS_INFO_URL} for more info.` | ||
); | ||
} | ||
|
||
logger.debug( | ||
`Setting environment variable(local) ${localEnvironmentVariable} to ${localEnvironment[localEnvironmentVariable]}.` | ||
); | ||
process.env[localEnvironmentVariable] = | ||
localEnvironment[localEnvironmentVariable]; | ||
}); | ||
}; | ||
|
||
module.exports = { | ||
loadEnvironmentVariables, | ||
}; |
Oops, something went wrong.